package com.cloudkinto.service.stock.impl;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.api.SkuAdjustApiBo;
import com.cloudkinto.bo.api.SkuAdjustItemApiBo;
import com.cloudkinto.bo.stock.*;
import com.cloudkinto.bo.warehouse.WarehouseQueryBo;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.ExcelExportTitleStyle;
import com.cloudkinto.common.RabbitQueueConstant;
import com.cloudkinto.common.RedisKeyConstants;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.common.NumberFormater;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.MathUtils;
import com.cloudkinto.common.utils.TimeUtils;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.dto.stock.NormalInputShelfDto;
import com.cloudkinto.dto.stock.TransferInputShelfDto;
import com.cloudkinto.dto.stock.adjust.box.BoxCellDto;
import com.cloudkinto.dto.stock.adjust.box.BoxCellStockDto;
import com.cloudkinto.dto.stock.adjust.box.BoxForAdjustDto;
import com.cloudkinto.dto.stock.adjust.sku.SkuCellDto;
import com.cloudkinto.dto.stock.adjust.sku.SkuCellStockDto;
import com.cloudkinto.dto.stock.back.OrderReturnStockDto;
import com.cloudkinto.dto.stock.defective.SkuPickQueryBo;
import com.cloudkinto.dto.stock.defective.SkuStockForPickDto;
import com.cloudkinto.dto.stock.defective.SkuStockTotalDto;
import com.cloudkinto.dto.stock.query.*;
import com.cloudkinto.dto.stock.rent.StockAgeDto;
import com.cloudkinto.dto.stock.snap.StockAgeEntity;
import com.cloudkinto.dto.stock.stock.StockOmpExportByBox;
import com.cloudkinto.dto.stock.stock.StockOmpExportBySku;
import com.cloudkinto.dto.stock.stock.StockOmsExportByBox;
import com.cloudkinto.dto.stock.stock.StockOmsExportBySku;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.stock.StockOrderTypeEnum;
import com.cloudkinto.enumType.stock.StockTypeEnum;
import com.cloudkinto.enumType.storage.StorageAreaEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.consumer.vo.StockToPlatformConsumerReq;
import com.cloudkinto.service.consumer.vo.amazon.inventory.AmazonStockToPlatReq;
import com.cloudkinto.service.consumer.vo.rakuten.RakutenStockToPlatReq;
import com.cloudkinto.service.consumer.vo.yahoo.YahooStockToPlatReq;
import com.cloudkinto.service.cost.CostService;
import com.cloudkinto.service.platform.vo.amazon.stock.InventoryAmazonEntity;
import com.cloudkinto.service.platform.vo.product.StockToPlatformManualReq;
import com.cloudkinto.service.platform.vo.product.StockToPlatformReq;
import com.cloudkinto.service.platform.vo.rakuten.stock.InventoryRakutenReq;
import com.cloudkinto.service.platform.vo.yahoo.stock.YahooSkuSetStockReq;
import com.cloudkinto.service.product.ProductCategoryService;
import com.cloudkinto.service.product.ProductService;
import com.cloudkinto.service.stock.ProductStockFlowingService;
import com.cloudkinto.service.stock.ProductStockRackService;
import com.cloudkinto.service.stock.ProductStockService;
import com.cloudkinto.service.stock.StockHistoryDetailService;
import com.cloudkinto.service.stock.exportVo.ComplexStockExportVo;
import com.cloudkinto.service.stock.exportVo.SkuAdjustImportErrorVo;
import com.cloudkinto.service.stock.exportVo.SkuAdjustImportVo;
import com.cloudkinto.service.stock.vo.snap.ProductWeightReq;
import com.cloudkinto.service.warehouse.WarehouseService;
import com.cloudkinto.vo.cost.CostInfoEntity;
import com.cloudkinto.vo.cost.CostItemEt;
import com.cloudkinto.vo.order.inbound.OrderInputListProductVo;
import com.cloudkinto.vo.stock.*;
import com.cloudkinto.vo.stock.adjust.box.*;
import com.cloudkinto.vo.stock.adjust.sku.SkuAdjustInitReq;
import com.cloudkinto.vo.stock.adjust.sku.SkuAdjustItemReq;
import com.cloudkinto.vo.stock.adjust.sku.SkuAdjustReq;
import com.cloudkinto.vo.stock.move.BoxMoveItemReq;
import com.cloudkinto.vo.stock.move.BoxMoveRackReq;
import com.cloudkinto.vo.stock.move.BoxMoveReq;
import com.cloudkinto.vo.stock.move.sku.SkuMoveItemReq;
import com.cloudkinto.vo.stock.move.sku.SkuMoveReq;
import com.cloudkinto.vo.storage.LocationRes;
import com.cloudkinto.vo.storage.LocationTypeVo;
import com.cloudkinto.vo.storage.StorageLocationVo;
import com.cloudkinto.vo.warehouse.WarehouseRes;
import com.cloudkinto.vo.wr.QsItemDetailVoVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author hua
 * @since 2024-10-28
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class ProductStockServiceImpl extends ServiceImpl<ProductStockDao, ProductStockDo> implements ProductStockService {

    private final QuotationSchemeDao quotationDao;
    private final QuotationItemDao quotationItemDao;
    private final CostService costService;
    private final WarehouseRentDao warehouseRentDao;
    private final WarehouseRentDetailDao warehouseRentDetailDao;
    private final StockHistoryDetailService stockHistoryDetailService;
    private final WarehouseService warehouseService;
    private final CompanyService companyService;
    private final ProductService productService;
    private final ProductCategoryService productCategoryService;
    private final OrderBoxDao boxDao;
    private final OrderBoxProductDao boxProductDao;
    private final ProductStockRackService productStockRackService;
    private final ProductStockRackDao productStockRackDao;
    private final ProductStockFlowingService productStockFlowingService;
    private final StorageLocationDao storageLocationDao;
    private final StorageAreaDao storageAreaDao;
    private final OrderInputDao orderInputDao;
    private final PlatformStoreDao platformStoreDao;
    private final RabbitTemplate rabbitTemplate;
    private final RedisTemplate redisTemplate;
    private final AsyncJobDao asyncJobDao;
    @Value("${gbc.customer}")
    private String customer;

    @Override
    public List<OrderReturnStockDto> getReturnStockRecord(Long orderId) {
        List<OrderReturnStockDto> returnStockRecord = baseMapper.getReturnStockRecord(orderId, StockOrderTypeEnum.Return.getValue());
        for (OrderReturnStockDto et : returnStockRecord) {
            et.setAreaTypeName(StorageAreaEnum.getAreaTypeName(et.getAreaType()));
        }
        return returnStockRecord;
    }


    @Override
    public SingleResult pageInit(Long userId, Long tenantId, Long companyId, String source) {
        HashMap<String, Object> map = new HashMap<>(8);
        map.put("productCategoryList", productCategoryService.getCategoryTree(tenantId));
        map.put("storageAreaType", StorageAreaEnum.getMapList());
        if ("oms".equals(source)) {
            map.put("warehouseList", warehouseService.getWarehouseCompany(companyId, tenantId));
        } else {
            map.put("companyList", companyService.getCompanyList(userId, tenantId, source));
            map.put("warehouseList", warehouseService.getList(tenantId));
        }
        map.put("orderTypeList", StockOrderTypeEnum.getStockOrderTypeEnum());
        return SingleResult.success(map);
    }


    @Override
    public List<TransferInputShelfDto> getTransferInputShelfData(List<Long> pasIdList) {
        return baseMapper.getTransferInputShelfData(
                pasIdList,
                StockOrderTypeEnum.Shelf.getValue(),
                StockTypeEnum.two.getValue()
        );
    }

    @Override
    public Collection<NormalInputShelfDto> getNormalInputShelfData(List<Long> pasIdList) {
        Collection<NormalInputShelfDto> normalInputSkuShelfData = baseMapper.getNormalInputSkuShelfData(
                pasIdList,
                StockOrderTypeEnum.Shelf.getValue(),
                StockTypeEnum.one.getValue()
        );
//        Collection<NormalInputShelfDto> normalInputBoxShelfData = baseMapper.getNormalInputBoxShelfData(
//                pasIdList,
//                StockOrderTypeEnum.Shelf.getValue(),
//                StockTypeEnum.three.getValue()
//        );
//        return Stream.concat(normalInputSkuShelfData.stream(), normalInputBoxShelfData.stream()).collect(Collectors.toList());
        return normalInputSkuShelfData;
    }

    private void addCheck(ProductWeightReq req) {
        if ((MathUtils.add(req.getThreeDaysWeight(), req.getSevenDaysWeight(), req.getThirtyDaysWeight())) != 1d) {
            throw new BizException("权重之和不等于1");
        }
    }

    @Override
    public Object addWeight(ProductWeightReq req, Long userId, Long companyId) {
        if (req.getCode() != 1 && (req.getProductIds() == null || req.getProductIds().isEmpty())) {
            return null;
        }
        this.addCheck(req);
        List<ProductDo> asinDoList;
        if (req.getCode() == 1) {
            asinDoList = productService.list(new QueryWrapper<ProductDo>().eq("company_id", companyId));
        } else {
            asinDoList = productService.list(new QueryWrapper<ProductDo>().in("id", req.getProductIds()));
        }
        if (asinDoList.isEmpty()) {
            return null;
        }
        LambdaUpdateWrapper<ProductDo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(ProductDo::getId, asinDoList.stream().map(ProductDo::getId).collect(Collectors.toList()));
        updateWrapper.set(ProductDo::getThreeDaysWeight, req.getThreeDaysWeight());
        updateWrapper.set(ProductDo::getSevenDaysWeight, req.getSevenDaysWeight());
        updateWrapper.set(ProductDo::getThirtyDaysWeight, req.getThirtyDaysWeight());
        productService.update(updateWrapper);
        return "设置成功";
    }


    /**
     * 产品的库存状态
     *
     * @param map
     * @return
     */
    @Override
    public PageResult<SkuStockDto> productBySku(Map<String, Object> map) {
        int currentPage = map.get("current") != null ? Integer.parseInt(map.get("current").toString()) : 1;
        int pageSize = (map.get("pageSize") != null) ? Integer.parseInt(map.get("pageSize").toString()) : 10;
        PageHelper.startPage(currentPage, pageSize);
        //todo  库存减去待处理的订单数量
        List<SkuStockDto> list = baseMapper.productBySku(map);
        PageInfo<SkuStockDto> pageInfo = new PageInfo<>(list);
        PageResult<SkuStockDto> result = new PageResult<SkuStockDto>(list, currentPage, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
        return result;
    }


    @Override
    public PageResult<SkuStockPageDto> productListPageBySku(ProductStockBySkuQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<SkuStockPageDto> list = baseMapper.productListPageBySku(bo);
        PageInfo<SkuStockPageDto> pageInfo = new PageInfo<>(list);
        List<Long> productIdList = list.stream().map(SkuStockPageDto::getProductId).distinct().collect(Collectors.toList());
        List<ProductTransportVo> productTransportVos = new ArrayList<>();
        Map<Long, ProductDo> productIdMatchDo = new HashMap<>(productIdList.size() * 4 / 3 + 2);
        if (CollUtil.isNotEmpty(productIdList)) {
            if (StrUtil.isNotBlank(bo.getSource()) && "oms".equals(bo.getSource())) {
                productTransportVos = baseMapper.getTransportNumBySku(productIdList, bo.getWhIdList());
            }
            productIdMatchDo = productService.lambdaQuery()
                    .in(ProductDo::getId, productIdList)
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        return new PageResult<>(this.handlerProductListPageBySkuAfter(list, bo.getSource(), productTransportVos, productIdMatchDo), current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public PageResult productListPageByLocation(ProductStockByLocationQueryBo req) {
        Integer current = req.getCurrent();
        Integer pageSize = req.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<SkuCellStockPageDto> list = baseMapper.productListPageByLocation(req);
        PageInfo<SkuCellStockPageDto> pageInfo = new PageInfo<>(list);
        return new PageResult<>(this.handlerProductListPageByLocationAfter(list), current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public PageResult boxListPageByType(BoxStockByTypeQueryBo req) {
        Integer current = req.getCurrent();
        Integer pageSize = req.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<BoxTypeStockPageDto> list = baseMapper.boxListPageByType(req);
        PageInfo<BoxTypeStockPageDto> pageInfo = new PageInfo<>(list);
        return new PageResult<>(this.handlerBoxListPageByTypeAfter(list, req.getSource()), current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public PageResult boxListPageByLocation(BoxStockByLocationQueryBo req) {
        Integer current = req.getCurrent();
        Integer pageSize = req.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<BoxCellStockPageDto> list = baseMapper.boxListPageByLocation(req);
        PageInfo<BoxCellStockPageDto> pageInfo = new PageInfo<>(list);
        return new PageResult<>(this.handlerBoxListPageByLocationAfter(list), current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    private List<BoxTypeStockPageDto> handlerBoxListPageByTypeAfter(List<BoxTypeStockPageDto> list, String system) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        List<Long> companyIdList = list.stream().map(BoxTypeStockPageDto::getCompanyId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        Set<Long> whIdList = list.stream().map(BoxTypeStockPageDto::getWhId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, CompanyDo> companyDoMap = companyService.list(
                Wrappers.<CompanyDo>lambdaQuery().in(CollUtil.isNotEmpty(companyIdList), CompanyDo::getId, companyIdList)
        ).stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.list(
                Wrappers.<WarehouseDo>lambdaQuery().in(CollUtil.isNotEmpty(whIdList), WarehouseDo::getId, whIdList)
        ).stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<String, OrderBoxDo> boxTypeMatchDo = new HashMap<>(list.size() * 4 / 3 + 2);
        if (StrUtil.isNotBlank(system) && "omp".equals(system)) {
            boxTypeMatchDo = boxDao.selectList(
                    Wrappers.<OrderBoxDo>lambdaQuery()
                            .in(OrderBoxDo::getBoxTypeNumber, list.stream().map(BoxTypeStockPageDto::getBoxTypeNumber).distinct().collect(Collectors.toList()))
            ).stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (val1, val2) -> val1));
        }
        for (BoxTypeStockPageDto dto : list) {
            dto.setCompanyName(companyDoMap.get(dto.getCompanyId()).getName());
            dto.setCompanyCode(companyDoMap.get(dto.getCompanyId()).getCompanyCode());
            dto.setWhCode(whIdMatch.get(dto.getWhId()).getWhCode());
            dto.setWhNameCn(whIdMatch.get(dto.getWhId()).getWhNameCn());
            if (Objects.nonNull(dto.getCreateTime())) {
                dto.setAgeDay(Math.toIntExact(DateUtil.betweenDay(dto.getCreateTime(), DateUtil.date(), true)));
            } else {
                dto.setAgeDay(0);
            }
            if (StrUtil.isNotBlank(system) && "omp".equals(system)) {
                OrderBoxDo boxDo = boxTypeMatchDo.get(dto.getBoxTypeNumber());
                if (Objects.nonNull(boxDo)) {
                    BigDecimal volume = boxDo.getLength()
                            .multiply(boxDo.getWidth())
                            .multiply(boxDo.getHeight())
                            .divide(BigDecimal.valueOf(1000000), 3, RoundingMode.HALF_UP);
                    if (volume.compareTo(BigDecimal.valueOf(0.001)) < 0) {
                        volume = BigDecimal.valueOf(0.001);
                    }
                    BigDecimal weight = boxDo.getWeight();
                    if (dto.getTotalAmount() > 0) {
                        dto.setTotalWeight(weight.multiply(BigDecimal.valueOf(dto.getTotalAmount())).setScale(3, RoundingMode.HALF_UP));
                        dto.setTotalVolume(volume.multiply(BigDecimal.valueOf(dto.getTotalAmount())).setScale(3, RoundingMode.HALF_UP));
                    }
                    if (dto.getAvailableAmount() > 0) {
                        dto.setAvailableWeight(weight.multiply(BigDecimal.valueOf(dto.getAvailableAmount())).setScale(3, RoundingMode.HALF_UP));
                        dto.setAvailableVolume(volume.multiply(BigDecimal.valueOf(dto.getAvailableAmount())).setScale(3, RoundingMode.HALF_UP));
                    }
                    if (dto.getLockAmount() > 0) {
                        dto.setLockWeight(weight.multiply(BigDecimal.valueOf(dto.getLockAmount())).setScale(3, RoundingMode.HALF_UP));
                        dto.setLockVolume(volume.multiply(BigDecimal.valueOf(dto.getLockAmount())).setScale(3, RoundingMode.HALF_UP));
                    }
                }
            }
        }
        return list;
    }

    private List<BoxCellStockPageDto> handlerBoxListPageByLocationAfter(List<BoxCellStockPageDto> list) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        List<Long> companyIdList = list.stream().map(BoxCellStockPageDto::getCompanyId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, CompanyDo> companyDoMap = companyService.list(
                Wrappers.<CompanyDo>lambdaQuery().in(CollUtil.isNotEmpty(companyIdList), CompanyDo::getId, companyIdList)
        ).stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        for (BoxCellStockPageDto dto : list) {
            dto.setCompanyName(companyDoMap.get(dto.getCompanyId()).getName());
            dto.setCompanyCode(companyDoMap.get(dto.getCompanyId()).getCompanyCode());
        }
        return list;
    }


    @Override
    public void getStockAgeList() {
        //获取月份
        Map<Long, String> currencyCodeMap = warehouseService.getList(new WarehouseQueryBo()).stream().collect(Collectors.toMap(WarehouseRes::getId, WarehouseRes::getCurrencyCode));
        int month = DateUtil.thisMonth() + 1;
        List<StockAgeDto> stockAgeList = baseMapper.getStockAgeList();
        Map<String, List<StockAgeDto>> stockMap = stockAgeList.stream().collect(Collectors.groupingBy(i -> i.getTenantId() + "-" + i.getCompanyId() + "-" + i.getWhId()));
        List<StockAgeEntity> stockEntityList = new ArrayList<>();
        for (String key : stockMap.keySet()) {
            Long companyId = Long.parseLong(key.split("-")[1]);
            Long tenantId = Long.parseLong(key.split("-")[0]);
            Long whId = Long.parseLong(key.split("-")[2]);
            CostInfoEntity costDo = new CostInfoEntity();
            costDo.setStatus(0);
            costDo.setModuleType(StaticDict.Cost_ModuleType.Third.getValue());
            costDo.setWhId(whId);
            costDo.setCompanyId(companyId);
            costDo.setTenantId(tenantId);
            costDo.setCurrencyCode(currencyCodeMap.get(whId));

            List<StockAgeDto> stockAgeDtos = stockMap.get(key);
            //获取报价方案
            QuotationSchemeDo qsId = quotationDao.getQsByCompanyId(companyId, whId);
            if (qsId == null) {
                costDo.setCostStatus(1);
                costService.insertEntity(costDo, null);
                stockEntityList.addAll(BeanConvert.INSTANCE.stockAgeEntity(stockAgeDtos));
                continue;
            }
            costDo.setQuotationId(qsId.getId());
            costDo.setCostTime(new Date());
            costDo.setCostStatus(2);

            List<QsItemDetailVoVo> list = quotationItemDao.list(qsId.getId(), whId, tenantId, 2);
            Map<Integer, Double> rentProductMap = new HashMap<>();//key 是endAge  value是 值
            Map<Integer, Double> rentBoxMap = new HashMap<>();//key 是endAge  value是 值
            WarehouseRentDo productRent = null;
            WarehouseRentDo boxRent = null;

            //仓储费
            for (QsItemDetailVoVo qsItem : list) {
                WarehouseRentDo rentDo = warehouseRentDao.selectById(qsItem.getThirdId());
                if (rentDo == null) {
                    continue;
                }
                if (rentDo.getRentType() == 0) {
                    productRent = rentDo;
                } else if (rentDo.getRentType() == 1) {
                    boxRent = rentDo;
                }
                List<WarehouseRentDetailDo> rentList = warehouseRentDetailDao.selectList(new LambdaQueryWrapper<WarehouseRentDetailDo>()
                        .eq(WarehouseRentDetailDo::getRentId, rentDo.getId()));
                for (WarehouseRentDetailDo detailDo : rentList) {
                    if (rentDo.getRentType() == 0) {
                        rentProductMap.put(detailDo.getEndAge(), detailDo.getRentFee(month));
                    } else if (rentDo.getRentType() == 1) {
                        rentBoxMap.put(detailDo.getEndAge(), detailDo.getRentFee(month));
                    }
                }
            }
            List<CostItemEt> costItemEtList = new ArrayList<>();
            //去计算 计费体积
            if (productRent == null) {
                stockEntityList.addAll(BeanConvert.INSTANCE.stockAgeEntity(stockAgeDtos.stream().filter(i -> i.getType() == 1).collect(Collectors.toList())));
            } else {
                List<StockAgeEntity> productEntityList = this.getStockEntity(productRent, stockAgeDtos, rentProductMap, 1);
                double totalFee = productEntityList.stream().mapToDouble(i -> i.getCalVolume() * i.getUnitRentFee()).sum();

                double totalVolume = productEntityList.stream().mapToDouble(StockAgeEntity::getVolume).sum();
                double totalCalV = productEntityList.stream().mapToDouble(StockAgeEntity::getCalVolume).sum();
                costItemEtList.add(new CostItemEt("产品库存仓租", totalFee, productRent.getCalculateUnit(), productRent.getCalculateWay(), totalVolume, totalCalV));
                stockEntityList.addAll(productEntityList);
            }

            if (boxRent == null) {
                stockEntityList.addAll(BeanConvert.INSTANCE.stockAgeEntity(stockAgeDtos.stream().filter(i -> i.getType() == 2).collect(Collectors.toList())));
            } else {
                List<StockAgeEntity> productEntityList = this.getStockEntity(boxRent, stockAgeDtos, rentBoxMap, 2);
                double totalFee = productEntityList.stream().mapToDouble(i -> i.getCalVolume() * i.getUnitRentFee()).sum();

                double totalVolume = productEntityList.stream().mapToDouble(StockAgeEntity::getVolume).sum();
                double totalCalV = productEntityList.stream().mapToDouble(StockAgeEntity::getCalVolume).sum();
                costItemEtList.add(new CostItemEt("箱库存仓租", totalFee, boxRent.getCalculateUnit(), boxRent.getCalculateWay(), totalVolume, totalCalV));
                stockEntityList.addAll(productEntityList);
            }

            costDo.setCostTotal(BigDecimal.valueOf(costItemEtList.stream().mapToDouble(CostItemEt::getTotalFee).sum()));
            costDo.setItems(costItemEtList);
            costService.insertEntity(costDo, null);

        }
        //再去插入库存快照
        List<List<StockAgeEntity>> partition = ListUtil.partition(stockEntityList, 5000);
        for (List<StockAgeEntity> stockAgeEntities : partition) {
            stockHistoryDetailService.insertBatchMy(stockAgeEntities);
        }

    }


    public List<StockAgeEntity> getStockEntity(WarehouseRentDo rentDo, List<StockAgeDto> stockAgeDtos, Map<Integer, Double> rentMap, int type) {
        List<StockAgeEntity> productEntityList = new ArrayList<>();
        stockAgeDtos.stream().filter(i -> i.getType() == type).forEach(i -> {
            StockAgeEntity entity = BeanConvert.INSTANCE.stockAgeEntity(i);
            //产品库存
            double calVolume = i.getLength().doubleValue() * i.getWidth().doubleValue() * i.getHeight().doubleValue() / 1000000;
            double calVolumeBs = i.getLengthBs().doubleValue() * i.getWidthBs().doubleValue() * i.getHeightBs().doubleValue() / 1728;
            if (rentDo.getCalculateUnit() == 0) {
                entity.setVolume(calVolume * i.getQuantity());
            } else {
                entity.setVolume(calVolumeBs * i.getQuantity());
            }
            if (rentDo.getMinBillVolume() != null) {
                calVolume = Math.max(calVolume, rentDo.getMinBillVolume());
                calVolumeBs = Math.max(calVolumeBs, rentDo.getMinBillVolume());
            }
            if (rentDo.getCalculateUnit() == 0) {
                entity.setCalVolume(calVolume * i.getQuantity());
            } else {
                entity.setCalVolume(calVolumeBs * i.getQuantity());
            }
            entity.setCalculateUnit(rentDo.getCalculateUnit());

            int end = 0;
            for (Integer endAge : rentMap.keySet()) {
                if (endAge == 0) continue;
                if (endAge >= i.getAge()) {
                    end = endAge;
                    break;
                }
            }
            entity.setUnitRentFee(rentMap.get(end));
            productEntityList.add(entity);
        });
        return productEntityList;
    }

    private List<SkuStockPageDto> handlerProductListPageBySkuAfter(List<SkuStockPageDto> list,
                                                                   String system,
                                                                   List<ProductTransportVo> productTransportVoList,
                                                                   Map<Long, ProductDo> productIdMatchDo) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        Set<Long> whIdList = list.stream().map(SkuStockPageDto::getWhId).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> companyIdList = list.stream().map(SkuStockPageDto::getCompanyId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, WarehouseDo> whIdMatch = warehouseService.list(
                Wrappers.<WarehouseDo>lambdaQuery().in(CollUtil.isNotEmpty(whIdList), WarehouseDo::getId, whIdList)
        ).stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, CompanyDo> companyDoMap = companyService.list(
                Wrappers.<CompanyDo>lambdaQuery().in(CollUtil.isNotEmpty(companyIdList), CompanyDo::getId, companyIdList)
        ).stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        //根据仓库加产品id匹配在途数量
        Map<String, Integer> whIdAndProductMatchTransportNum = productTransportVoList.stream().collect(Collectors.toMap(key -> key.getWhId() + "-" + key.getProductId(), ProductTransportVo::getQty));
        Map<String, Integer> reserveFlagTransportNum = productTransportVoList.stream().collect(Collectors.toMap(key -> key.getWhId() + "-" + key.getProductId(), ProductTransportVo::getReservedQty));
        for (SkuStockPageDto skuStockPageDto : list) {
            if (StringUtils.isBlank(skuStockPageDto.getWhCode()) && whIdMatch.get(skuStockPageDto.getWhId()) != null) {
                skuStockPageDto.setWhCode(whIdMatch.get(skuStockPageDto.getWhId()).getWhCode());
            }
            skuStockPageDto.setWhNameCn(whIdMatch.get(skuStockPageDto.getWhId()).getWhNameCn());
            skuStockPageDto.setCompanyName(companyDoMap.get(skuStockPageDto.getCompanyId()).getName());
            skuStockPageDto.setCompanyCode(companyDoMap.get(skuStockPageDto.getCompanyId()).getCompanyCode());
            ProductDo productDo1 = productIdMatchDo.get(skuStockPageDto.getProductId());
            if (productDo1 == null) continue;
            skuStockPageDto.setDeclarePrice(productDo1.getDeclarePrice());
            if (StrUtil.isNotBlank(system) && "oms".equals(system)) {
                if (skuStockPageDto.getStockAttribute().equals(0)) {
                    Integer qty = whIdAndProductMatchTransportNum.getOrDefault(skuStockPageDto.getWhId() + "-" + skuStockPageDto.getProductId(), 0);
                    skuStockPageDto.setTransportAmount(qty);
                    skuStockPageDto.setInTransitQty(reserveFlagTransportNum.getOrDefault(skuStockPageDto.getWhId() + "-" + skuStockPageDto.getProductId(), 0));
                }
            }
            if (StrUtil.isNotBlank(system) && "omp".equals(system)) {
                //计算单个sku重量及体积
                BigDecimal volume = productDo1.getWmsLength() == null ? productDo1.getLength() : productDo1.getWmsLength()
                        .multiply(productDo1.getWmsWidth() == null ? productDo1.getWidth() : productDo1.getWmsWidth())
                        .multiply(productDo1.getWmsHeight() == null ? productDo1.getHeight() : productDo1.getWmsHeight())
                        .divide(BigDecimal.valueOf(1000000), 3, RoundingMode.HALF_UP);
                if (volume.compareTo(BigDecimal.valueOf(0.001)) < 0) {
                    volume = BigDecimal.valueOf(0.001);
                }
                BigDecimal weight = productDo1.getWmsWeight() == null ? productDo1.getWeight() : productDo1.getWmsWeight();
                if (skuStockPageDto.getTotalAmount() > 0) {
                    skuStockPageDto.setTotalWeight(weight.multiply(BigDecimal.valueOf(skuStockPageDto.getTotalAmount())).setScale(3, RoundingMode.HALF_UP));
                    skuStockPageDto.setTotalVolume(volume.multiply(BigDecimal.valueOf(skuStockPageDto.getTotalAmount())).setScale(3, RoundingMode.HALF_UP));
                }
                if (skuStockPageDto.getAvailableAmount() > 0) {
                    skuStockPageDto.setAvailableWeight(weight.multiply(BigDecimal.valueOf(skuStockPageDto.getAvailableAmount())).setScale(3, RoundingMode.HALF_UP));
                    skuStockPageDto.setAvailableVolume(volume.multiply(BigDecimal.valueOf(skuStockPageDto.getAvailableAmount())).setScale(3, RoundingMode.HALF_UP));
                }
                if (skuStockPageDto.getLockAmount() > 0) {
                    skuStockPageDto.setLockWeight(weight.multiply(BigDecimal.valueOf(skuStockPageDto.getLockAmount())).setScale(3, RoundingMode.HALF_UP));
                    skuStockPageDto.setLockVolume(volume.multiply(BigDecimal.valueOf(skuStockPageDto.getLockAmount())).setScale(3, RoundingMode.HALF_UP));
                }
            }
        }
        return list;
    }

    private List<SkuCellStockPageDto> handlerProductListPageByLocationAfter(List<SkuCellStockPageDto> list) {
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        Set<Long> companyIdList = list.stream().map(SkuCellStockPageDto::getCompanyId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, CompanyDo> companyDoMap = companyService.list(
                Wrappers.<CompanyDo>lambdaQuery().in(CollUtil.isNotEmpty(companyIdList), CompanyDo::getId, companyIdList)
        ).stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        for (SkuCellStockPageDto skuCellStockPageDto : list) {
            skuCellStockPageDto.setCompanyName(companyDoMap.get(skuCellStockPageDto.getCompanyId()).getName());
            skuCellStockPageDto.setCompanyCode(companyDoMap.get(skuCellStockPageDto.getCompanyId()).getCompanyCode());
        }
        return list;
    }


    /**
     * 综合库存
     *
     * @param map
     * @return
     */
    @Override
    public PageResult complexStockPage(Map<String, Object> map) {
        IPage<ComplexStockPageDto> page = this.pageInit(map);
        page = baseMapper.complexStockPage(page, map);
        PageResult result = new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    @Override
    public ComplexStockTotalDto complexStock(Map<String, Object> map) {
        ComplexStockTotalDto totalDto = baseMapper.complexStockTotal(map);
        return totalDto;
    }

    @Override
    public void complexStockExport(Map<String, Object> map, HttpServletResponse response) {
        List<ComplexStockPageDto> list = baseMapper.complexStockPage(map);
        List<ComplexStockExportVo> exportVos = BeanConvert.INSTANCE.complexStockExportVo(list);
        String fileName = "综合库存" + TimeUtils.getCurrentDateFormat("MMddHHmmss");
        try {
            ExcelUtils.exportExcel(exportVos, ComplexStockExportVo.class, fileName, new ExportParams(), response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断库存够不够
     */
    @Override
    public boolean jugSkuStock(Map<Long, Integer> skuQtyMap, int stockAttribute, Long storageId, Long companyId) {
        Map<String, Object> map = new HashMap<>();
        map.put("productIds", new ArrayList<>(skuQtyMap.keySet()));
        map.put("source", "oms");
        map.put("companyId", companyId);
        map.put("stockAttribute", stockAttribute);
        map.put("storageIds", Arrays.asList(storageId));
        List<ComplexStockPageDto> complexStockPageDtos = baseMapper.complexStockPage(map);
        if (complexStockPageDtos.isEmpty()) {
            return false;
        }
        Map<Long, ComplexStockPageDto> collect = complexStockPageDtos.stream().collect(Collectors.toMap(ComplexStockPageDto::getProductId, i -> i, (i1, i2) -> i1));
        for (Long productId : skuQtyMap.keySet()) {
            ComplexStockPageDto complexStockPageDto = collect.get(productId);
            if (complexStockPageDto == null || complexStockPageDto.getProductQty() < skuQtyMap.get(productId)) {
                return false;
            }
        }
        return true;
    }


    private IPage<ComplexStockPageDto> pageInit(Map<String, Object> map) {
        IPage<ComplexStockPageDto> page = new Page<>();
        page.setSize(map.get("pageSize") != null ? Long.parseLong(map.get("pageSize").toString()) : 10);
        page.setCurrent(map.get("current") != null ? Long.parseLong(map.get("current").toString()) : 1);
        return page;
    }


    /****************************************************************库存调整 移库等等*******************************************************/

    /**
     * 箱库存 调整移库初始化 获取库位可用数量
     */
    @Override
    public List<BoxCellStockDto> boxCellStockList(BoxAdjustInitReq req, Long whId, Long tenantId) {
        List<String> boxTypeNumber = req.getBoxes().stream().map(BoxCellStockDto::getBoxTypeNumber).filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> rackIds = req.getBoxes().stream().map(BoxCellStockDto::getRackId).filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> companyIds = req.getBoxes().stream().map(BoxCellStockDto::getCompanyId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, StorageLocationDo> rackMap = new HashMap<>();
        Map<Long, String> areaMap = new HashMap<>();
        if (!rackIds.isEmpty()) {
            rackMap = storageLocationDao.selectBatchIds(rackIds).stream().collect(Collectors.toMap(StorageLocationDo::getId, i -> i));
        }
        List<Long> areaIds = rackMap.values().stream().map(StorageLocationDo::getAreaId).collect(Collectors.toList());
        if (!areaIds.isEmpty()) {
            areaMap = storageAreaDao.selectBatchIds(areaIds).stream().collect(Collectors.toMap(StorageAreaDo::getId, StorageAreaDo::getCode));
        }
        if (boxTypeNumber.isEmpty() || rackIds.isEmpty() || companyIds.isEmpty()) return req.getBoxes();
        List<BoxCellStockDto> boxCellStock = baseMapper.getBoxCellStock(boxTypeNumber, rackIds, companyIds, whId, tenantId);
        Map<String, BoxCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
        for (BoxCellStockDto box : req.getBoxes()) {
            String key = box.getBoxTypeNumber() + "_____" + box.getRackId() + "_____" + box.getCompanyId();
            if (rackMap.get(box.getRackId()) != null) {
                box.setCellNo(rackMap.get(box.getRackId()).getCode());
                box.setAreaCode(areaMap.get(rackMap.get(box.getRackId()).getAreaId()));
            }
            box.setRackId(box.getRackId());
            box.setProductList(orderInputDao.getBoxProductListByBoxTypeNumber(box.getBoxTypeNumber(), box.getCompanyId()));
            BoxCellStockDto boxCellStockDto = boxMap.get(key);
            if (boxCellStockDto == null) continue;
            box.setQuantity(boxCellStockDto.getQuantity());
        }
        return req.getBoxes();
    }

    private IPage<BoxForAdjustDto> boxPageInit(Map<String, Object> map) {
        IPage<BoxForAdjustDto> page = new Page<>();
        page.setSize(map.get("pageSize") != null ? Long.parseLong(map.get("pageSize").toString()) : 10);
        page.setCurrent(map.get("current") != null ? Long.parseLong(map.get("current").toString()) : 1);
        return page;
    }


    /**
     * 添加箱子按钮
     */
    @Override
    public PageResult getBoxListForAdjust(Map<String, Object> map) {
        IPage<BoxForAdjustDto> page = boxPageInit(map);
        page = baseMapper.getBoxListForAdjust(page, map);
        PageResult result = new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }


    /**
     * 箱库存 批量库存调整
     */
    @Override
    public List<AdjustItemRes> boxStockAdjust(BoxAdjustReq req, Long whId, Long userId, Long tenantId) {
        List<AdjustItemRes> resList = new ArrayList<>();
        List<String> boxTypeNumber = req.getBoxes().stream().map(BoxAdjustItemReq::getBoxTypeNumber).filter(Objects::nonNull).collect(Collectors.toList());
        if (boxTypeNumber.isEmpty()) return resList;
        Map<Integer, List<BoxAdjustItemReq>> adjustTypeMap = req.getBoxes().stream().collect(Collectors.groupingBy(BoxAdjustItemReq::getAdjustType));
        if (adjustTypeMap.get(1) != null) {
            //减少
            List<String> keys = new ArrayList<>();
            //将减少的 按箱类和库位 分
            Map<String, List<BoxAdjustItemReq>> reqBoxMap = adjustTypeMap.get(1).stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
            for (String key : reqBoxMap.keySet()) {
                //获取调整数量 和 调整类型 备注
                int sum = reqBoxMap.get(key).stream().mapToInt(BoxAdjustItemReq::getAdjustQty).sum();
                if (sum == 0) continue;
                keys.add(key);
            }
            List<Long> reduceRackId = new ArrayList<>();
            List<String> reduceNumber = new ArrayList<>();
            List<Long> reduceCompanyIds = new ArrayList<>();
            for (String key : keys) {
                Long sourceRackId = Long.parseLong(key.split("_____")[1]);
                Long companyId = Long.parseLong(key.split("_____")[2]);
                reduceCompanyIds.add(companyId);
                reduceRackId.add(sourceRackId);
                reduceNumber.add(key.split("_____")[0]);
            }
            List<BoxCellStockDto> boxCellStock = baseMapper.getBoxCellStock(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId);
            Map<String, BoxCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                int sum = reqBoxMap.get(key).stream().mapToInt(BoxAdjustItemReq::getAdjustQty).sum();
                if (sum == 0) {
                    keys.remove(i);
                    i--;
                    continue;
                }
                //判断可用库存是否足够
                if (boxMap.get(key) == null) {
                    resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                    keys.remove(i);
                    i--;
                    continue;
                }
                int rackLeftQty = boxMap.get(key).getQuantity();
                if (sum > rackLeftQty) {
                    //说明库存不够
                    resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                    keys.remove(i);
                    i--;
                    continue;
                }
            }
            if (!resList.isEmpty()) {
                //说明有不符合条件的
                return resList;
            }
            Map<String, List<BoxCellDto>> stockMap = new HashMap<>();
            if (!reduceNumber.isEmpty()) {
                stockMap = baseMapper.getBoxCellList(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId).stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
            }
            for (String key : keys) {
                //获取调整数量 和 调整类型 备注
                int quantity = reqBoxMap.get(key).stream().mapToInt(BoxAdjustItemReq::getAdjustQty).sum();
                if (quantity == 0) continue;
                //根据 库位 箱类 获取库存List
                List<BoxCellDto> boxList = stockMap.get(key);
                String tips = reqBoxMap.get(key).get(0).getTips();
                this.reduceStock(boxList, key.split("_____")[0], quantity, StockOrderTypeEnum.Adjust.getValue(), tips, whId, tenantId);
            }
        }
        if (adjustTypeMap.get(0) != null) {
            List<Long> rackIds = adjustTypeMap.get(0).stream().map(BoxAdjustItemReq::getRackId).collect(Collectors.toList());
            Map<Long, Integer> rackMap = storageLocationDao.getRackType(rackIds).stream().collect(Collectors.toMap(LocationTypeVo::getId, LocationTypeVo::getAreaType));
            //增加
            Map<String, List<BoxAdjustItemReq>> reqBoxMap = adjustTypeMap.get(0).stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
            for (String key : reqBoxMap.keySet()) {
                for (BoxAdjustItemReq boxItem : reqBoxMap.get(key)) {
                    ProductStockDo stockDo = new ProductStockDo(boxItem.getCompanyId(), whId, StockOrderTypeEnum.Adjust.getValue(), null, boxItem.getBoxTypeNumber(), boxItem.getAdjustQty(), StockTypeEnum.two.getValue(), tenantId);
                    baseMapper.insert(stockDo);
                    ProductStockRackDo rackDo = new ProductStockRackDo(stockDo.getId(), boxItem.getRackId(), StorageAreaEnum.three.getValue().equals(rackMap.get(boxItem.getRackId())) ? 1 : 0, boxItem.getAdjustQty(), boxItem.getAdjustQty());
                    productStockRackDao.insert(rackDo);
                    //库存流水
                    productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(boxItem.getBoxTypeNumber()).companyId(boxItem.getCompanyId())
                            .warehouseId(whId).rackId(boxItem.getRackId()).stockAttribute(rackDo.getStockAttribute())
                            .bookQuantity(boxItem.getAdjustQty()).orderType(StockOrderTypeEnum.Adjust.getValue()).noteNo("--").tips(boxItem.getTips())
                            .tenantId(tenantId).type(StockTypeEnum.two.getValue())
                            .build());
                }

            }
        }
        return resList;
    }


    @Override
    public int updateStockById(Long id, Long childId, int qty) {
        int i = productStockRackService.updateStockById(childId, qty);
        if (i > 0) {
            baseMapper.updateStockById(id);
        }
        return i;
    }


    /**
     * 箱库存-批量移库
     */

    @Override
    public List<AdjustItemRes> boxMoveStock(BoxMoveReq req, Long whId, Long userId, Long tenantId) {
        //todo removeCellNo
        List<AdjustItemRes> resList = new ArrayList<>();

        List<String> keys = new ArrayList<>();
        //req 里的信息 先按照 boxTypeNumber 和rackId 合并
        Map<String, List<BoxMoveItemReq>> reqBoxMap = req.getBoxes().stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
        for (String key : reqBoxMap.keySet()) {
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(i -> i.getRacks().stream().filter(z -> !Objects.equals(sourceRackId, z.getTargetRackId())).mapToInt(BoxMoveRackReq::getAdjustQty).sum()).sum();
            if (sum == 0) continue;
            keys.add(key);
        }
        List<Long> reduceRackId = new ArrayList<>();
        List<String> reduceNumber = new ArrayList<>();
        List<Long> reduceCompanyIds = new ArrayList<>();
        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            Long companyId = Long.parseLong(key.split("_____")[2]);
            reduceCompanyIds.add(companyId);
            reduceRackId.add(sourceRackId);
            reduceNumber.add(key.split("_____")[0]);
        }
        if (reduceNumber.isEmpty() || reduceRackId.isEmpty() || reduceCompanyIds.isEmpty()) return null;
        List<BoxCellStockDto> boxCellStock = baseMapper.getBoxCellStock(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId);
        Map<String, BoxCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(x -> x.getRacks().stream().filter(z -> !Objects.equals(sourceRackId, z.getTargetRackId())).mapToInt(BoxMoveRackReq::getAdjustQty).sum()).sum();
            if (sum == 0) {
                keys.remove(i);
                i--;
                continue;
            }
            //判断可用库存是否足够
            if (boxMap.get(key) == null) {
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                keys.remove(i);
                i--;
                continue;
            }
            int rackLeftQty = boxMap.get(key).getQuantity();
            if (sum > rackLeftQty) {
                //说明库存不够
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                keys.remove(i);
                i--;
                continue;
            }
        }
        if (!resList.isEmpty()) {
            //说明有不符合条件的
            return resList;
        }
        Map<String, List<BoxCellDto>> stockMap = baseMapper.getBoxCellList(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId).stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
        Set<Long> rackIds = new HashSet<>();
        for (BoxMoveItemReq box : req.getBoxes()) {
            rackIds.add(box.getRackId());
            for (BoxMoveRackReq rack : box.getRacks()) {
                rackIds.add(rack.getTargetRackId());
            }
        }
        Map<Long, Integer> rackAttributeMap = storageLocationDao.getList(new ArrayList<>(rackIds), tenantId).stream().collect(Collectors.toMap(StorageLocationVo::getId, StorageLocationVo::getMainType));
        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            String boxTypeNumber = key.split("_____")[0];
            Map<String, Integer> moveMap = new HashMap<>();
            Map<String, String> tipsMap = new HashMap<>();//todo
            List<Long> targetRackIds = new ArrayList<>();

            List<BoxMoveItemReq> reqItemList = reqBoxMap.get(key);
            for (BoxMoveItemReq boxMoveItemReq : reqItemList) {
                for (BoxMoveRackReq rack : boxMoveItemReq.getRacks()) {
                    if (Objects.equals(rack.getTargetRackId(), sourceRackId)) {
                        continue;
                    }
                    targetRackIds.add(rack.getTargetRackId());
                    tipsMap.put(rack.getTargetRackId() + "_____" + rackAttributeMap.get(rack.getTargetRackId()) + "_____" + boxMoveItemReq.getCompanyId(), rack.getTips());
                    moveMap.merge(rack.getTargetRackId() + "_____" + rackAttributeMap.get(rack.getTargetRackId()) + "_____" + boxMoveItemReq.getCompanyId(), rack.getAdjustQty(), Integer::sum);
                }
            }
            Map<Long, Integer> rackMap = storageLocationDao.getRackType(targetRackIds).stream().collect(Collectors.toMap(LocationTypeVo::getId, LocationTypeVo::getAreaType));
            int quantity = moveMap.values().stream().mapToInt(Integer::intValue).sum();
            this.removeStock(stockMap.get(key), moveMap, tipsMap, rackMap, boxTypeNumber, quantity, StockOrderTypeEnum.Move.getValue(), whId, tenantId);
        }
        return null;
    }


    /**
     * 减少库存
     */
    public void reduceStock(List<BoxCellDto> boxList, String boxTypeNumber, int quantity, String orderType, String tips, Long whId, Long tenantId) {
        if (quantity == 0) return;
        if (boxList == null || boxList.isEmpty()) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
        boxList.sort(new Comparator<BoxCellDto>() {
            @Override
            public int compare(BoxCellDto o1, BoxCellDto o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (BoxCellDto sourceStock : boxList) {
            int reduceQty = 0;
            if (quantity > sourceStock.getQuantity()) {
                reduceQty = sourceStock.getQuantity();
                quantity -= sourceStock.getQuantity();
            } else {
                reduceQty = quantity;
                quantity = 0;
            }
            if (reduceQty == 0) {
                continue;
            }
            //更新库存
            int i = this.updateStockById(sourceStock.getId(), sourceStock.getChildId(), reduceQty);
            if (i > 0) { // 库存流水
                String batchNo = DateUtils.dateFormatToString(sourceStock.getCreateTime(), "yyyyMMdd");
                productStockFlowingService.insertProForOut(ProductFlowingOutEt.builder().sku(sourceStock.getBoxTypeNumber()).companyId(sourceStock.getCompanyId())
                        .warehouseId(whId).rackId(sourceStock.getRackId()).stockAttribute(sourceStock.getStockAttribute())
                        .bookQuantity(-reduceQty).orderType(StockOrderTypeEnum.Adjust.getValue()).sourceOrderType(sourceStock.getOrderType()).noteNo("--").innerBatchNo(batchNo).tips(tips)
                        .tenantId(tenantId).type(StockTypeEnum.two.getValue()).build());
            }
            if (quantity == 0) {
                break;
            }
        }
        if (quantity > 0) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
    }


    public void removeStock(List<BoxCellDto> boxList, Map<String, Integer> moveMap, Map<String, String> tipsMap, Map<Long, Integer> rackMap, String boxTypeNumber, int quantity, String orderType, Long whId, Long tenantId) {
        if (quantity == 0) return;
        if (boxList == null || boxList.isEmpty()) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
        boxList.sort(new Comparator<BoxCellDto>() {
            @Override
            public int compare(BoxCellDto o1, BoxCellDto o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (BoxCellDto sourceStock : boxList) {
            int reduceQty = 0;
            if (quantity > sourceStock.getQuantity()) {
                reduceQty = sourceStock.getQuantity();
                quantity -= sourceStock.getQuantity();
            } else {
                reduceQty = quantity;
                quantity = 0;
            }
            if (reduceQty == 0) {
                continue;
            }
            int sourceRemoveQty = reduceQty;
            //更新库存
            int i = productStockRackService.updateStockById(sourceStock.getChildId(), reduceQty);
            if (i > 0) { // 库存流水
                //不更新主表
                //再去插入目标库位
                StringBuilder builderTips = new StringBuilder();
                for (String moveKey : moveMap.keySet()) {
                    Long targetRackId = Long.parseLong(moveKey.split("_____")[0]);
                    int stockAtt = sourceStock.getStockAttribute();
                    String attribute = moveKey.split("_____")[1];
                    if (!"null".equals(attribute)) {
                        stockAtt = Integer.parseInt(attribute);
                    }
                    Long companyId = Long.parseLong(moveKey.split("_____")[2]);
                    Integer toMoveQty = moveMap.get(moveKey);
                    if (toMoveQty == null || toMoveQty <= 0) {
                        continue;
                    }
                    int thisMoveQty = 0;
                    if (reduceQty > toMoveQty) {
                        thisMoveQty = toMoveQty;
                        reduceQty -= toMoveQty;
                    } else {
                        thisMoveQty = reduceQty;
                        reduceQty = 0;
                    }
                    moveMap.put(moveKey, toMoveQty);
                    ProductStockRackDo rackDo = new ProductStockRackDo(sourceStock.getId(), targetRackId, stockAtt, thisMoveQty, thisMoveQty);
                    productStockRackDao.insert(rackDo);
                    //库存流水
                    productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(boxTypeNumber).companyId(companyId)
                            .warehouseId(whId).rackId(targetRackId).stockAttribute(stockAtt).tips(tipsMap.get(moveKey))
                            .bookQuantity(thisMoveQty).orderType(StockOrderTypeEnum.Move.getValue()).noteNo("--")
                            .tenantId(tenantId).type(StockTypeEnum.two.getValue())
                            .build());
                    if (builderTips.length() > 0) {
                        builderTips.append(",");
                    }
                    builderTips.append(tipsMap.get(moveKey));
                    if (reduceQty == 0) {
                        break;
                    }
                }
                if (reduceQty > 0) {
                    throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
                }
                String batchNo = DateUtils.dateFormatToString(sourceStock.getCreateTime(), "yyyyMMdd");
                productStockFlowingService.insertProForOut(ProductFlowingOutEt.builder().sku(sourceStock.getBoxTypeNumber()).companyId(sourceStock.getCompanyId())
                        .warehouseId(whId).rackId(sourceStock.getRackId()).stockAttribute(sourceStock.getStockAttribute()).tips(builderTips.toString())
                        .bookQuantity(-sourceRemoveQty).orderType(orderType).noteNo("--").innerBatchNo(batchNo)
                        .tenantId(tenantId).type(StockTypeEnum.two.getValue()).build());
            }
            if (quantity == 0) {
                break;
            }
        }
        if (quantity > 0) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
    }


    /**
     * 产品库存-库存调整初始化
     *
     * @param req
     * @param whId
     * @param tenantId
     * @return
     */
    @Override
    public List<SkuCellStockDto> skuCellStockList(SkuAdjustInitReq req, Long whId, Long tenantId) {
        List<Long> productIds = req.getBoxes().stream().map(SkuCellStockDto::getProductId).filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> rackIds = req.getBoxes().stream().map(SkuCellStockDto::getRackId).filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> companyIds = req.getBoxes().stream().map(SkuCellStockDto::getCompanyId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, StorageLocationDo> rackMap = new HashMap<>();
        Map<Long, String> areaMap = new HashMap<>();
        if (!rackIds.isEmpty()) {
            rackMap = storageLocationDao.selectBatchIds(rackIds).stream().collect(Collectors.toMap(StorageLocationDo::getId, i -> i));
        }
        List<Long> areaIds = rackMap.values().stream().map(StorageLocationDo::getAreaId).collect(Collectors.toList());
        if (!areaIds.isEmpty()) {
            areaMap = storageAreaDao.selectBatchIds(areaIds).stream().collect(Collectors.toMap(StorageAreaDo::getId, StorageAreaDo::getCode));
        }
        if (productIds.isEmpty()) return req.getBoxes();
        List<SkuCellStockDto> boxCellStock = baseMapper.getSkuCellStock(productIds, rackIds, companyIds, null, whId, tenantId);
        Map<String, SkuCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
        for (SkuCellStockDto sku : req.getBoxes()) {
            String key = sku.getProductId() + "_____" + sku.getStockAttribute() + "_____" + sku.getRackId() + "_____" + sku.getCompanyId();
            sku.setRackId(sku.getRackId());
            if (rackMap.get(sku.getRackId()) != null) {
                sku.setCellNo((rackMap.get(sku.getRackId()).getCode()));
                sku.setAreaCode(areaMap.get(rackMap.get(sku.getRackId()).getAreaId()));
            }
            SkuCellStockDto boxCellStockDto = boxMap.get(key);
            if (boxCellStockDto == null) continue;
            sku.setQuantity(boxCellStockDto.getQuantity());
        }
        return req.getBoxes();
    }

    /**
     * api 不穿库位信息 默认
     *
     * @return
     */
    @Override
    public List<AdjustItemRes> skuStockAdjustApi(SkuAdjustApiBo bo, Long whId, Long userId, Long tenantId) {
        if (whId == null) {
            throw new BizException("仓库不存在");
        }
        if (CollUtil.isEmpty(bo.getSkus())) {
            return new ArrayList<>();
        }
        //获取库位信息
        LocationRes goodLocationRes = storageLocationDao.getPickingRecommendRack(0, whId);
        LocationRes badLocationRes = storageLocationDao.getPickingRecommendRack(1, whId);
        //获取产品信息 根据sku获取productId
        Map<Long, List<SkuAdjustItemApiBo>> companyMap = bo.getSkus().stream().filter(i -> i.getCompanyId() != null).collect(Collectors.groupingBy(SkuAdjustItemApiBo::getCompanyId));
        Map<String, Long> productIdMap = new HashMap<>();
        for (Long companyId : companyMap.keySet()) {
            List<ProductDo> list = productService.lambdaQuery().select(ProductDo::getId, ProductDo::getSku).eq(ProductDo::getCompanyId, companyId).in(ProductDo::getSku, companyMap.get(companyId).stream().map(SkuAdjustItemApiBo::getSku).collect(Collectors.toList())).list();
            productIdMap.putAll(list.stream().collect(Collectors.toMap(ProductDo::getSku, ProductDo::getId)));
        }
        List<SkuAdjustItemReq> skus = new ArrayList<>();
        for (SkuAdjustItemApiBo itemApiBo : bo.getSkus()) {
//            SkuAdjustItemReq itemReq = new SkuAdjustItemReq();
//            BeanUtils.copyProperties(itemApiBo, itemReq);
            SkuAdjustItemReq itemReq = BeanConvert.INSTANCE.skuAdjustItemApiBoToSkuAdjustItemReq(itemApiBo);
            if (itemReq.getCompanyId() == null) {
                throw new BizException(SysConstant.Company_NotExist);
            }
            itemReq.setProductId(productIdMap.get(itemReq.getSku()));
            if (itemReq.getProductId() == null || StringUtils.isBlank(itemReq.getSku())) {
                throw new BizException(SysConstant.Company_Not_Exist_Sku);
            }
            if (itemReq.getStockAttribute() == 0) {
                //绝对值调整 适配所有库存
                if (goodLocationRes == null) {
                    throw new BizException("无库位信息");
                }
                itemReq.setRackId(goodLocationRes.getId());
                itemReq.setCellNo(goodLocationRes.getCellNo());
            }
            if (itemReq.getStockAttribute() == 1) {
                if (badLocationRes == null) {
                    throw new BizException("无库位信息");
                }
                itemReq.setRackId(badLocationRes.getId());
                itemReq.setCellNo(badLocationRes.getCellNo());
            }
            if (itemApiBo.getAdjustType() == 2 && itemApiBo.getAdjustQty() < 0) {
                continue;
            }
            skus.add(itemReq);
        }
        SkuAdjustReq req = new SkuAdjustReq();
        req.setSkus(skus);
        return this.skuStockAdjust(req, whId, userId, tenantId);
    }

    /**
     * sku库存 批量库存调整
     */
    @Override
    public List<AdjustItemRes> skuStockAdjust(SkuAdjustReq req, Long whId, Long userId, Long tenantId) {
        if (whId == null) {
            return new ArrayList<>();
        }
        List<AdjustItemRes> resList = new ArrayList<>();
        List<String> boxTypeNumber = req.getSkus().stream().map(SkuAdjustItemReq::getSku).filter(Objects::nonNull).collect(Collectors.toList());
        if (boxTypeNumber.isEmpty()) return resList;
        Map<Integer, List<SkuAdjustItemReq>> adjustTypeMap = req.getSkus().stream().collect(Collectors.groupingBy(SkuAdjustItemReq::getAdjustType));
        if (adjustTypeMap.get(1) != null) {
            //减少
            List<String> keys = new ArrayList<>();
            //将减少的 按箱类和库位 分
            Map<String, List<SkuAdjustItemReq>> reqBoxMap = adjustTypeMap.get(1).stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            for (String key : reqBoxMap.keySet()) {
                //获取调整数量 和 调整类型 备注
                int sum = reqBoxMap.get(key).stream().mapToInt(SkuAdjustItemReq::getAdjustQty).sum();
                if (sum == 0) continue;
                keys.add(key);
            }
            List<Long> reduceProductIds = new ArrayList<>();
            List<Long> reduceCompanyIds = new ArrayList<>();
            for (String key : keys) {
                Long companyId = Long.parseLong(key.split("_____")[2]);
                reduceCompanyIds.add(companyId);
                reduceProductIds.add(Long.parseLong(key.split("_____")[0]));
            }
            List<SkuCellStockDto> boxCellStock = new ArrayList<>();
            if (!reduceProductIds.isEmpty()) {
                boxCellStock = baseMapper.getSkuCellStock(reduceProductIds, null, reduceCompanyIds, null, whId, tenantId);
            }
            Map<String, List<SkuCellStockDto>> boxMap = boxCellStock.stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                int sum = reqBoxMap.get(key).stream().mapToInt(SkuAdjustItemReq::getAdjustQty).sum();
                if (sum == 0) {
                    keys.remove(i);
                    i--;
                    continue;
                }
                //判断可用库存是否足够
                if (boxMap.get(key) == null) {
                    resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getSku(), "库存不足"));
                    keys.remove(i);
                    i--;
                    continue;
                }
                int rackLeftQty = boxMap.get(key).stream().filter(q -> q.getQuantity() != null && q.getQuantity() > 0).mapToInt(SkuCellStockDto::getQuantity).sum();
                if (sum > rackLeftQty) {
                    //说明库存不够
                    resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getSku(), "库存不足"));
                    keys.remove(i);
                    i--;
                    continue;
                }
            }
            if (!resList.isEmpty()) {
                //说明有不符合条件的
                return resList;
            }
            Map<String, List<SkuCellDto>> stockMap = new HashMap<>();
            if (!reduceProductIds.isEmpty()) {
                stockMap = baseMapper.getSkuCellList(reduceProductIds, null, reduceCompanyIds, whId, tenantId).stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            }
            for (String key : keys) {
                //获取调整数量 和 调整类型 备注
                int quantity = reqBoxMap.get(key).stream().mapToInt(SkuAdjustItemReq::getAdjustQty).sum();
                if (quantity == 0) continue;
                //根据 库位 箱类 获取库存List
                List<SkuCellDto> boxList = stockMap.get(key);
                String tips = reqBoxMap.get(key).get(0).getTips();
                this.reduceSku(boxList, key.split("_____")[0], quantity, StockOrderTypeEnum.Adjust.getValue(), tips, whId, tenantId);
            }
        }
        if (adjustTypeMap.get(0) != null) {
            //增加
            Map<String, List<SkuAdjustItemReq>> reqBoxMap = adjustTypeMap.get(0).stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
            for (String key : reqBoxMap.keySet()) {
                for (SkuAdjustItemReq boxItem : reqBoxMap.get(key)) {
                    if (boxItem.getAdjustQty() == 0) continue;
                    int stockAttribute = Integer.parseInt(key.split("_____")[1]);
                    ProductStockDo stockDo = new ProductStockDo(boxItem.getCompanyId(), whId, StockOrderTypeEnum.Adjust.getValue(), null, Long.parseLong(key.split("_____")[0]), boxItem.getAdjustQty(), StockTypeEnum.one.getValue(), tenantId);
                    baseMapper.insert(stockDo);
                    ProductStockRackDo rackDo = new ProductStockRackDo(stockDo.getId(), boxItem.getRackId(), stockAttribute, boxItem.getAdjustQty(), boxItem.getAdjustQty());
                    productStockRackDao.insert(rackDo);
                    //库存流水
                    productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(boxItem.getSku()).companyId(boxItem.getCompanyId())
                            .warehouseId(whId).rackId(boxItem.getRackId()).stockAttribute(rackDo.getStockAttribute())
                            .bookQuantity(boxItem.getAdjustQty()).orderType(StockOrderTypeEnum.Adjust.getValue()).noteNo("--").tips(boxItem.getTips())
                            .tenantId(tenantId).type(StockTypeEnum.one.getValue())
                            .build());
                }

            }
        }
        if (adjustTypeMap.get(2) != null) {
            //绝对值
            Map<Long, String> skuIdMap = adjustTypeMap.get(2).stream().collect(Collectors.toMap(SkuAdjustItemReq::getProductId, SkuAdjustItemReq::getSku));
            //将减少的 按箱类和库位 分
            Map<String, List<SkuAdjustItemReq>> reqBoxMap = adjustTypeMap.get(2).stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            //获取调整数量 和 调整类型 备注
            //                int sum = reqBoxMap.get(key).stream().mapToInt(SkuAdjustItemReq::getAdjustQty).sum();
            //                if (sum == 0) continue;
            List<String> keys = new ArrayList<>(reqBoxMap.keySet());
            List<Long> reduceProductIds = new ArrayList<>();
            List<Long> reduceCompanyIds = new ArrayList<>();
            for (String key : keys) {
                Long companyId = Long.parseLong(key.split("_____")[2]);
                reduceCompanyIds.add(companyId);
                reduceProductIds.add(Long.parseLong(key.split("_____")[0]));
            }
            List<SkuCellStockDto> boxCellStock = baseMapper.getSkuCellStock(reduceProductIds, null, reduceCompanyIds, null, whId, tenantId);
            Map<String, List<SkuCellStockDto>> boxMap = boxCellStock.stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            Map<String, Integer> adjustMap = new HashMap<>();
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                int sum = reqBoxMap.get(key).stream().mapToInt(SkuAdjustItemReq::getAdjustQty).sum();
                int rackLeftQty = boxMap.get(key) == null ? 0 : boxMap.get(key).stream().filter(q -> q.getQuantity() != null && q.getQuantity() > 0).mapToInt(SkuCellStockDto::getQuantity).sum();
                adjustMap.put(key, sum - rackLeftQty);
            }
            if (!resList.isEmpty()) {
                //说明有不符合条件的
                return resList;
            }
            Map<String, List<SkuCellDto>> stockMap = new HashMap<>();
            if (!reduceProductIds.isEmpty()) {
                stockMap = baseMapper.getSkuCellList(reduceProductIds, null, reduceCompanyIds, whId, tenantId).stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getCompanyId()));
            }
            for (String key : keys) {
                //获取调整数量 和 调整类型 备注
                Integer quantity = adjustMap.get(key);
                if (quantity == null || quantity == 0) continue;
                String tips = reqBoxMap.get(key).get(0).getTips();
                if (quantity < 0) {
                    //根据 库位 箱类 获取库存List
                    List<SkuCellDto> boxList = stockMap.get(key);
                    this.reduceSku(boxList, key.split("_____")[0], -quantity, StockOrderTypeEnum.Adjust.getValue(), tips, whId, tenantId);
                } else {
                    Long companyId = Long.parseLong(key.split("_____")[2]);
                    Long productId = Long.parseLong(key.split("_____")[0]);
                    ProductStockDo stockDo = new ProductStockDo(companyId, whId, StockOrderTypeEnum.Adjust.getValue(), null, productId, quantity, StockTypeEnum.one.getValue(), tenantId);
                    baseMapper.insert(stockDo);
                    //获取推荐库位
                    Integer stockAttribute = Integer.parseInt(key.split("_____")[1]);
                    ProductStockRackDo rackDo = new ProductStockRackDo(stockDo.getId(), reqBoxMap.get(key).get(0).getRackId(), stockAttribute, quantity, quantity);
                    productStockRackDao.insert(rackDo);
                    //库存流水
                    productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(skuIdMap.get(productId)).companyId(companyId)
                            .warehouseId(whId).rackId(reqBoxMap.get(key).get(0).getRackId()).stockAttribute(rackDo.getStockAttribute())
                            .bookQuantity(quantity).orderType(StockOrderTypeEnum.Adjust.getValue()).noteNo("--").tips(tips)
                            .tenantId(tenantId).type(StockTypeEnum.one.getValue())
                            .build());
                }
            }

        }
        return resList;
    }


    @Override
    public Map<String, Object> importSkuForAdjust(MultipartFile file, Long companyId) {
        CompanyDo companyDo = companyService.getById(companyId);
        if (companyDo == null) {
            throw new BizException("公司不存在");
        }
        List<SkuAdjustImportVo> skuAdjustImportVoList = null;
        try {
            skuAdjustImportVoList = ExcelUtils.importExcel(file, 0, 1, SkuAdjustImportVo.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //匹配sku
        List<String> skuList = skuAdjustImportVoList.stream().map(SkuAdjustImportVo::getSku).collect(Collectors.toList());
        Map<String, ProductDo> productMap = new HashMap<>();
        if (!skuList.isEmpty()) {
            productMap = productService.list(new LambdaQueryWrapper<ProductDo>()
                    .eq(ProductDo::getCompanyId, companyId).in(ProductDo::getSku, skuList)).stream().collect(Collectors.toMap(ProductDo::getSku, i -> i, (v1, v2) -> v1));
        }
        List<SkuCellStockDto> resList = new ArrayList<>();

        List<SkuAdjustImportErrorVo> errorVoList = new ArrayList<>();
        for (SkuAdjustImportVo skuAdjustImportVo : skuAdjustImportVoList) {
            if (skuAdjustImportVo.getAdjustQty() == 0) {
                continue;
            }
            ProductDo productDo = productMap.get(skuAdjustImportVo.getSku());
            if (productDo == null) {
                errorVoList.add(new SkuAdjustImportErrorVo(skuAdjustImportVo.getSku(), "sku不存在"));
                continue;
            }
            //正品
            resList.add(new SkuCellStockDto(companyId, companyDo.getCompanyName(), companyDo.getCompanyCode(), productDo.getId(),
                    skuAdjustImportVo.getSku(), productDo.getProductName(), 0, skuAdjustImportVo.getAdjustQty(), skuAdjustImportVo.getTips()));
        }
        Map<String, Object> map = new HashMap<>();
        map.put("successList", resList);
        map.put("errorList", errorVoList);
        return map;
    }

    /**
     * 减少库存
     */
    public void reduceSku(List<SkuCellDto> boxList, String boxTypeNumber, int quantity, String orderType, String tips, Long whId, Long tenantId) {
        if (quantity == 0) return;
        if (boxList == null || boxList.isEmpty()) {
            ProductDo productDo = productService.getById(boxTypeNumber);
            throw new BizException(SysConstant.Sku_Stock_NotEnough, productDo == null ? boxTypeNumber : productDo.getSku());
        }
        boxList.sort(new Comparator<SkuCellDto>() {
            @Override
            public int compare(SkuCellDto o1, SkuCellDto o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (SkuCellDto sourceStock : boxList) {
            int reduceQty = 0;
            if (quantity > sourceStock.getQuantity()) {
                reduceQty = sourceStock.getQuantity();
                quantity -= sourceStock.getQuantity();
            } else {
                reduceQty = quantity;
                quantity = 0;
            }
            if (reduceQty == 0) {
                continue;
            }
            //更新库存
            int i = this.updateStockById(sourceStock.getId(), sourceStock.getChildId(), reduceQty);
            if (i > 0) { // 库存流水
                String batchNo = DateUtils.dateFormatToString(sourceStock.getCreateTime(), "yyyyMMdd");
                productStockFlowingService.insertProForOut(ProductFlowingOutEt.builder().sku(sourceStock.getSku()).companyId(sourceStock.getCompanyId())
                        .warehouseId(whId).rackId(sourceStock.getRackId()).stockAttribute(sourceStock.getStockAttribute())
                        .bookQuantity(-reduceQty).orderType(orderType).sourceOrderType(sourceStock.getOrderType()).noteNo("--").innerBatchNo(batchNo).tips(tips)
                        .tenantId(tenantId).type(StockTypeEnum.one.getValue()).build());
            }
            if (quantity == 0) {
                break;
            }
        }
        if (quantity > 0) {
            ProductDo productDo = productService.getById(boxTypeNumber);
            throw new BizException(SysConstant.Sku_Stock_NotEnough, productDo == null ? boxTypeNumber : productDo.getSku());
        }
    }


    /**
     * 箱库存-批量移库
     */

    @Override
    public List<AdjustItemRes> skuMoveStock(SkuMoveReq req, Long whId, Long userId, Long tenantId) {
        List<AdjustItemRes> resList = new ArrayList<>();

        List<String> keys = new ArrayList<>();
        //req 里的信息 先按照 boxTypeNumber 和rackId 合并
        Map<String, List<SkuMoveItemReq>> reqBoxMap = req.getSkus().stream().collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
        for (String key : reqBoxMap.keySet()) {
            Long sourceRackId = Long.parseLong(key.split("_____")[2]);
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(i -> i.getRacks().stream().filter(z -> !Objects.equals(sourceRackId, z.getTargetRackId())).mapToInt(BoxMoveRackReq::getAdjustQty).sum()).sum();
            if (sum == 0) continue;
            keys.add(key);
        }
        List<Long> reduceRackId = new ArrayList<>();
        List<Long> reduceNumber = new ArrayList<>();
        List<Long> reduceCompanyIds = new ArrayList<>();
        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[2]);
            Long companyId = Long.parseLong(key.split("_____")[3]);
            reduceCompanyIds.add(companyId);
            reduceRackId.add(sourceRackId);
            reduceNumber.add(Long.parseLong(key.split("_____")[0]));
        }
        if (reduceNumber.isEmpty() || reduceRackId.isEmpty() || reduceCompanyIds.isEmpty()) return null;
        List<SkuCellStockDto> boxCellStock = baseMapper.getSkuCellStock(reduceNumber, reduceRackId, reduceCompanyIds, null, whId, tenantId);
        Map<String, SkuCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Long sourceRackId = Long.parseLong(key.split("_____")[2]);
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(x -> x.getRacks().stream().filter(z -> !Objects.equals(sourceRackId, z.getTargetRackId())).mapToInt(BoxMoveRackReq::getAdjustQty).sum()).sum();
            if (sum == 0) {
                keys.remove(i);
                i--;
                continue;
            }
            //判断可用库存是否足够
            if (boxMap.get(key) == null) {
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getSku()));
                keys.remove(i);
                i--;
                continue;
            }
            int rackLeftQty = boxMap.get(key).getQuantity();
            if (sum > rackLeftQty) {
                //说明库存不够
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getSku()));
                keys.remove(i);
                i--;
                continue;
            }
        }
        if (!resList.isEmpty()) {
            //说明有不符合条件的
            return resList;
        }
        Map<String, List<SkuCellDto>> stockMap = baseMapper.getSkuCellList(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId).stream()
                .collect(Collectors.groupingBy(i -> i.getProductId() + "_____" + i.getStockAttribute() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
        //获取所有库位
        Set<Long> rackIds = new HashSet<>();
        for (SkuMoveItemReq skus : req.getSkus()) {
            rackIds.add(skus.getRackId());
            for (BoxMoveRackReq rack : skus.getRacks()) {
                rackIds.add(rack.getTargetRackId());
            }
        }
        Map<Long, Integer> rackAttributeMap = storageLocationDao.getList(new ArrayList<>(rackIds), tenantId).stream().collect(Collectors.toMap(StorageLocationVo::getId, StorageLocationVo::getMainType));
        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[2]);
            int stockAttribute = Integer.parseInt(key.split("_____")[1]);
            String boxTypeNumber = key.split("_____")[0];
            Map<String, Integer> moveMap = new HashMap<>();
            Map<String, String> tipsMap = new HashMap<>();//todo

            List<SkuMoveItemReq> reqItemList = reqBoxMap.get(key);
            for (SkuMoveItemReq boxMoveItemReq : reqItemList) {
                for (BoxMoveRackReq rack : boxMoveItemReq.getRacks()) {
                    if (Objects.equals(rack.getTargetRackId(), sourceRackId)) {
                        continue;
                    }
                    tipsMap.put(rack.getTargetRackId() + "_____" + rackAttributeMap.get(rack.getTargetRackId()) + "_____" + boxMoveItemReq.getCompanyId(), rack.getTips());
                    moveMap.merge(rack.getTargetRackId() + "_____" + rackAttributeMap.get(rack.getTargetRackId()) + "_____" + boxMoveItemReq.getCompanyId(), rack.getAdjustQty(), Integer::sum);
                }
            }
            int quantity = moveMap.values().stream().mapToInt(Integer::intValue).sum();
            this.removeSku(stockMap.get(key), moveMap, tipsMap, boxTypeNumber, quantity, StockOrderTypeEnum.Move.getValue(), whId, tenantId);
        }
        return null;
    }


    public void removeSku(List<SkuCellDto> boxList, Map<String, Integer> moveMap, Map<String, String> tipsMap, String boxTypeNumber, int quantity, String orderType, Long whId, Long tenantId) {
        if (quantity == 0) return;
        if (boxList == null || boxList.isEmpty()) {
            ProductDo productDo = productService.getById(boxTypeNumber);
            throw new BizException(SysConstant.Sku_Stock_NotEnough, productDo == null ? boxTypeNumber : productDo.getSku());
        }
        boxList.sort(new Comparator<SkuCellDto>() {
            @Override
            public int compare(SkuCellDto o1, SkuCellDto o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (SkuCellDto sourceStock : boxList) {
            int reduceQty = 0;
            if (quantity > sourceStock.getQuantity()) {
                reduceQty = sourceStock.getQuantity();
                quantity -= sourceStock.getQuantity();
            } else {
                reduceQty = quantity;
                quantity = 0;
            }
            if (reduceQty == 0) {
                continue;
            }
            int sourceRemoveQty = reduceQty;

            //更新库存
            int i = productStockRackService.updateStockById(sourceStock.getChildId(), reduceQty);
            if (i > 0) { // 库存流水
                //不更新主表
                //再去插入目标库位
                StringBuilder builderTips = new StringBuilder();
                for (String moveKey : moveMap.keySet()) {
                    Long targetRackId = Long.parseLong(moveKey.split("_____")[0]);
                    int stockAtt = sourceStock.getStockAttribute();
                    String attribute = moveKey.split("_____")[1];
                    if (!"null".equals(attribute)) {
                        stockAtt = Integer.parseInt(attribute);
                    }
                    Long companyId = Long.parseLong(moveKey.split("_____")[2]);
                    Integer toMoveQty = moveMap.get(moveKey);
                    if (toMoveQty == null || toMoveQty <= 0) {
                        continue;
                    }
                    int thisMoveQty = 0;
                    if (reduceQty > toMoveQty) {
                        thisMoveQty = toMoveQty;
                        reduceQty -= toMoveQty;
                    } else {
                        thisMoveQty = reduceQty;
                        reduceQty = 0;
                    }
                    toMoveQty -= thisMoveQty;
                    moveMap.put(moveKey, toMoveQty);
                    ProductStockRackDo rackDo = new ProductStockRackDo(sourceStock.getId(), targetRackId, stockAtt, thisMoveQty, thisMoveQty);
                    productStockRackDao.insert(rackDo);
                    //库存流水
                    productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(sourceStock.getSku()).companyId(companyId)
                            .warehouseId(whId).rackId(targetRackId).stockAttribute(stockAtt).tips(tipsMap.get(moveKey))
                            .bookQuantity(thisMoveQty).orderType(StockOrderTypeEnum.Move.getValue()).noteNo("--")
                            .tenantId(tenantId).type(StockTypeEnum.one.getValue())
                            .build());
                    if (builderTips.length() > 0) {
                        builderTips.append(",");
                    }
                    builderTips.append(tipsMap.get(moveKey));
                    if (reduceQty == 0) {
                        break;
                    }
                }
                if (reduceQty > 0) {
                    throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
                }
                String batchNo = DateUtils.dateFormatToString(sourceStock.getCreateTime(), "yyyyMMdd");
                productStockFlowingService.insertProForOut(ProductFlowingOutEt.builder().sku(sourceStock.getSku()).companyId(sourceStock.getCompanyId())
                        .warehouseId(whId).rackId(sourceStock.getRackId()).stockAttribute(sourceStock.getStockAttribute()).tips(builderTips.toString())
                        .bookQuantity(-sourceRemoveQty).orderType(orderType).noteNo("--").innerBatchNo(batchNo)
                        .tenantId(tenantId).type(StockTypeEnum.one.getValue()).build());
            }
            if (quantity == 0) {
                break;
            }
        }
        if (quantity > 0) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
    }


    /**
     * 箱库存- 库存转换
     */
    @Override
    public List<AdjustItemRes> boxConvert(BoxConvertReq req, Long whId, Long tenantId) {
        List<AdjustItemRes> resList = new ArrayList<>();

        List<String> keys = new ArrayList<>();
        //req 里的信息 先按照 boxTypeNumber 和rackId 合并
        Map<String, List<BoxConvertItemReq>> reqBoxMap = req.getBoxes().stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));
        for (String key : reqBoxMap.keySet()) {
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(BoxConvertItemReq::getConvertQty).sum();
            if (sum == 0) continue;
            keys.add(key);
        }
        List<Long> reduceRackId = new ArrayList<>();
        List<String> reduceNumber = new ArrayList<>();
        List<Long> reduceCompanyIds = new ArrayList<>();
        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            Long companyId = Long.parseLong(key.split("_____")[2]);
            reduceCompanyIds.add(companyId);
            reduceRackId.add(sourceRackId);
            reduceNumber.add(key.split("_____")[0]);
        }
        if (reduceNumber.isEmpty() || reduceRackId.isEmpty() || reduceCompanyIds.isEmpty()) return null;
        List<BoxCellStockDto> boxCellStock = baseMapper.getBoxCellStock(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId);
        Map<String, BoxCellStockDto> boxMap = boxCellStock.stream().collect(Collectors.toMap(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId(), i -> i));
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            //获取调整数量 和 调整类型 备注
            int sum = reqBoxMap.get(key).stream().mapToInt(BoxConvertItemReq::getConvertQty).sum();
            if (sum == 0) {
                keys.remove(i);
                i--;
                continue;
            }
            //判断可用库存是否足够
            if (boxMap.get(key) == null) {
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                keys.remove(i);
                i--;
                continue;
            }
            int rackLeftQty = boxMap.get(key).getQuantity();
            if (sum > rackLeftQty) {
                //说明库存不够
                resList.add(new AdjustItemRes(reqBoxMap.get(key).get(0).getCompanyName(), reqBoxMap.get(key).get(0).getCompanyCode(), reqBoxMap.get(key).get(0).getBoxTypeNumber()));
                keys.remove(i);
                i--;
                continue;
            }
        }
        if (!resList.isEmpty()) {
            //说明有不符合条件的
            return resList;
        }
        Map<String, List<BoxCellDto>> stockMap = baseMapper.getBoxCellList(reduceNumber, reduceRackId, reduceCompanyIds, whId, tenantId).stream().collect(Collectors.groupingBy(i -> i.getBoxTypeNumber() + "_____" + i.getRackId() + "_____" + i.getCompanyId()));

        for (String key : keys) {
            Long sourceRackId = Long.parseLong(key.split("_____")[1]);
            Long companyId = Long.parseLong(key.split("_____")[2]);

            String boxTypeNumber = key.split("_____")[0];
            //获取调整数量 和 调整类型 备注
            int quantity = reqBoxMap.get(key).stream().mapToInt(BoxConvertItemReq::getConvertQty).sum();
            if (quantity == 0) continue;
            //根据 库位 箱类 获取库存List
            List<BoxCellDto> boxList = stockMap.get(key);
            this.convertStock(boxList, boxTypeNumber, quantity, StockOrderTypeEnum.Convert.getValue(), reqBoxMap.get(key).get(0).getTips(), sourceRackId, companyId, whId, tenantId);
        }
        return null;
    }

    public void convertStock(List<BoxCellDto> boxList, String boxTypeNumber, int quantity, String orderType, String tips, Long sourceRackId, Long companyId, Long whId, Long tenantId) {
        int convertQty = quantity;
        if (quantity == 0) return;
        if (boxList == null || boxList.isEmpty()) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
        boxList.sort(new Comparator<BoxCellDto>() {
            @Override
            public int compare(BoxCellDto o1, BoxCellDto o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (BoxCellDto sourceStock : boxList) {
            int reduceQty = 0;
            if (quantity > sourceStock.getQuantity()) {
                reduceQty = sourceStock.getQuantity();
                quantity -= sourceStock.getQuantity();
            } else {
                reduceQty = quantity;
                quantity = 0;
            }
            if (reduceQty == 0) {
                continue;
            }
            //更新库存
            int i = this.updateStockById(sourceStock.getId(), sourceStock.getChildId(), reduceQty);
            if (i > 0) { // 库存流水
                String batchNo = DateUtils.dateFormatToString(sourceStock.getCreateTime(), "yyyyMMdd");
                productStockFlowingService.insertProForOut(ProductFlowingOutEt.builder().sku(sourceStock.getBoxTypeNumber()).companyId(sourceStock.getCompanyId())
                        .warehouseId(whId).rackId(sourceStock.getRackId()).stockAttribute(sourceStock.getStockAttribute())
                        .bookQuantity(-reduceQty).orderType(orderType).noteNo("--").innerBatchNo(batchNo).tips(tips)
                        .tenantId(tenantId).type(StockTypeEnum.two.getValue()).build());
            }
            if (quantity == 0) {
                break;
            }
        }
        if (quantity > 0) {
            throw new BizException(SysConstant.Box_Stock_NotEnough, boxTypeNumber);
        }
        //再去添加转换sku数量

        List<OrderInputListProductVo> inBoxSkuList = orderInputDao.getBoxProductListByBoxTypeNumber(boxTypeNumber, companyId);
        for (OrderInputListProductVo product : inBoxSkuList) {
            int convert = product.getQuantity() * convertQty;
            ProductStockDo stockDo = new ProductStockDo(companyId, whId, StockOrderTypeEnum.Convert.getValue(), null, product.getProductId(), convert, StockTypeEnum.one.getValue(), tenantId);
            baseMapper.insert(stockDo);
            ProductStockRackDo rackDo = new ProductStockRackDo(stockDo.getId(), sourceRackId, boxList.get(0).getStockAttribute(), convert, convert);
            productStockRackDao.insert(rackDo);
            //库存流水
            productStockFlowingService.insertProForIn(ProductFlowingInEt.builder().sku(product.getSku()).companyId(companyId)
                    .warehouseId(whId).rackId(sourceRackId).stockAttribute(rackDo.getStockAttribute()).tips(tips)
                    .bookQuantity(convert).orderType(StockOrderTypeEnum.Convert.getValue()).noteNo("--")
                    .tenantId(tenantId).type(StockTypeEnum.one.getValue())
                    .build());
        }

    }


    /**
     * 次品处理-要去锁定库存 获取可用库存列表 不合并
     *
     * @return
     */
    @Override
    public List<SkuStockForPickDto> getStockForPickList(SkuPickQueryBo bo) {
        Map<String, Object> map = new HashMap<>();
        map.put("type", bo.getType());
        map.put("stockAttribute", bo.getStockAttribute());
        map.put("productIds", bo.getProductId());
        map.put("whId", bo.getWhId());
        map.put("companyId", bo.getCompanyId());
        map.put("tenantId", bo.getTenantId());
        map.put("rackIds", bo.getRackIds());
        return baseMapper.getStockForPickList(map);
    }

    /**
     * 次品处理 新增编辑 获取sku可用库存列表(根据 group by t1.product_id,t3.sku, t3.product_name, t2.stock_attribute 合并)
     */
    @Override
    public PageResult getSkuStockTotalList(Map<String, Object> map) {
        map.put("type", 1);
        map.put("stockAttribute", 1);
        IPage<SkuStockTotalDto> page = this.pageInitD(map);
        page = baseMapper.getSkuStockTotalList(page, map);
        PageResult result = new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    @Override
    public List<SkuStockTotalDto> getSkuStockTotal(SkuPickQueryBo bo) {
        Map<String, Object> map = new HashMap<>();
        map.put("type", bo.getType());
        map.put("stockAttribute", bo.getStockAttribute());
        map.put("productIds", bo.getProductId());
        map.put("whId", bo.getWhId());
        map.put("companyId", bo.getCompanyId());
        map.put("tenantId", bo.getTenantId());
        List<SkuStockTotalDto> page = baseMapper.getSkuStockTotalList(map);
        return page;
    }

    @Override
    public void ompExportBySku(ProductStockBySkuQueryBo bo, HttpServletResponse response) throws IOException {
        List<SkuStockPageDto> list = baseMapper.productListPageBySku(bo);
        List<Long> productIdList = list.stream().map(SkuStockPageDto::getProductId).distinct().collect(Collectors.toList());
        List<ProductTransportVo> productTransportVos = new ArrayList<>();
        Map<Long, ProductDo> productIdMatchDo = new HashMap<>(productIdList.size() * 4 / 3 + 2);
        if (CollUtil.isNotEmpty(productIdList)) {
            if (StrUtil.isNotBlank(bo.getSource()) && "oms".equals(bo.getSource())) {
                productTransportVos = baseMapper.getTransportNumBySku(productIdList, bo.getWhIdList());
            }
            productIdMatchDo = productService.lambdaQuery()
                    .in(ProductDo::getId, productIdList)
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        this.handlerProductListPageBySkuAfter(list, bo.getSource(), productTransportVos, productIdMatchDo);
        List<StockOmpExportBySku> dtoList = new ArrayList<>(list.size());
        for (SkuStockPageDto pageDto : list) {
//            StockOmpExportBySku dto = new StockOmpExportBySku();
//            BeanUtils.copyProperties(pageDto, dto);
            StockOmpExportBySku dto = BeanConvert.INSTANCE.skuStockPageDtoToStockOmpExportBySku(pageDto);
            dto.setWhNameCode(pageDto.getWhNameCn() + "(" + pageDto.getWhCode() + ")");
            dto.setCompanyNameCode(pageDto.getCompanyName() + "(" + pageDto.getCompanyCode() + ")");
            dto.setStockAttribute(pageDto.getStockAttribute().equals(0) ? "正品" : "次品");
            ProductDo productDo = productIdMatchDo.get(pageDto.getProductId());
            if (productDo != null) {
//                BeanUtils.copyProperties(productDo, dto);
                BeanConvert.INSTANCE.productDoToStockOmpExportBySku(productDo, dto);
            }
            dtoList.add(dto);
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        ExcelUtils.exportExcel(dtoList, StockOmpExportBySku.class, "ProductInventory_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void omsExportBySku(ProductStockBySkuQueryBo bo, HttpServletResponse response) throws IOException {
        List<SkuStockPageDto> list = baseMapper.productListPageBySku(bo);
        List<Long> productIdList = list.stream().map(SkuStockPageDto::getProductId).distinct().collect(Collectors.toList());
        List<ProductTransportVo> productTransportVos = new ArrayList<>();
        Map<Long, ProductDo> productIdMatchDo = new HashMap<>(productIdList.size() * 4 / 3 + 2);
        if (CollUtil.isNotEmpty(productIdList)) {
            if (StrUtil.isNotBlank(bo.getSource()) && "oms".equals(bo.getSource())) {
                productTransportVos = baseMapper.getTransportNumBySku(productIdList, bo.getWhIdList());
            }
            productIdMatchDo = productService.lambdaQuery()
                    .in(ProductDo::getId, productIdList)
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        this.handlerProductListPageBySkuAfter(list, bo.getSource(), productTransportVos, productIdMatchDo);
        List<StockOmsExportBySku> dtoList = new ArrayList<>(list.size());
        for (SkuStockPageDto pageDto : list) {
//            StockOmsExportBySku dto = new StockOmsExportBySku();
//            BeanUtils.copyProperties(pageDto, dto);
            StockOmsExportBySku dto = BeanConvert.INSTANCE.skuStockPageDtoToStockOmsExportBySku(pageDto);
            dto.setWhNameCode(pageDto.getWhNameCn() + "(" + pageDto.getWhCode() + ")");
            dto.setStockAttribute(pageDto.getStockAttribute().equals(0) ? "正品" : "次品");
            ProductDo productDo = productIdMatchDo.get(pageDto.getProductId());
            if (productDo != null) {
//                BeanUtils.copyProperties(productDo, dto);
                BeanConvert.INSTANCE.productDoToStockOmsExportBySku(productDo, dto);
            }
            dtoList.add(dto);
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        ExcelUtils.exportExcel(dtoList, StockOmsExportBySku.class, "ProductInventory_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void ompExportByBoxTypeNo(BoxStockByTypeQueryBo bo, HttpServletResponse response) throws IOException {
        List<BoxTypeStockPageDto> list = baseMapper.boxListPageByType(bo);
        this.handlerBoxListPageByTypeAfter(list, bo.getSource());
        List<StockOmpExportByBox> dtoList = new ArrayList<>(list.size());
        for (BoxTypeStockPageDto pageDto : list) {
//            StockOmpExportByBox dto = new StockOmpExportByBox();
//            BeanUtils.copyProperties(pageDto, dto);
            StockOmpExportByBox dto = BeanConvert.INSTANCE.boxTypeStockPageDtoToStockOmpExportByBox(pageDto);
            dto.setWhNameCode(pageDto.getWhNameCn() + "(" + pageDto.getWhCode() + ")");
            dto.setCompanyNameCode(pageDto.getCompanyName() + "(" + pageDto.getCompanyCode() + ")");
            dto.setSize(pageDto.getLength() + "*" + pageDto.getWidth() + "*" + pageDto.getHeight() + "cm");
            for (OrderInputListProductVo productVo : pageDto.getProductList()) {
                StockOmpExportByBox dtoTemp = JSONUtil.toBean(JSONUtil.toJsonStr(dto), StockOmpExportByBox.class);
                dtoTemp.setSku(productVo.getSku());
                dtoTemp.setProductName(productVo.getProductName());
                dtoTemp.setPerBoxQty(productVo.getQuantity());
                dtoList.add(dtoTemp);
            }
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        ExcelUtils.exportExcel(dtoList, StockOmpExportByBox.class, "Omp_BoxInventory_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void omsExportByBoxTypeNo(BoxStockByTypeQueryBo bo, HttpServletResponse response) throws IOException {
        List<BoxTypeStockPageDto> list = baseMapper.boxListPageByType(bo);
        this.handlerBoxListPageByTypeAfter(list, bo.getSource());
        List<StockOmsExportByBox> dtoList = new ArrayList<>(list.size());
        for (BoxTypeStockPageDto pageDto : list) {
//            StockOmsExportByBox dto = new StockOmsExportByBox();
//            BeanUtils.copyProperties(pageDto, dto);
            StockOmsExportByBox dto = BeanConvert.INSTANCE.boxTypeStockPageDtoToStockOmsExportByBox(pageDto);
            dto.setWhNameCode(pageDto.getWhNameCn() + "(" + pageDto.getWhCode() + ")");
            dto.setSize(pageDto.getLength() + "*" + pageDto.getWidth() + "*" + pageDto.getHeight() + "cm");
            for (OrderInputListProductVo productVo : pageDto.getProductList()) {
                StockOmsExportByBox dtoTemp = JSONUtil.toBean(JSONUtil.toJsonStr(dto), StockOmsExportByBox.class);
                dtoTemp.setSku(productVo.getSku());
                dtoTemp.setProductName(productVo.getProductName());
                dtoTemp.setPerBoxQty(productVo.getQuantity());
                dtoList.add(dtoTemp);
            }
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        ExcelUtils.exportExcel(dtoList, StockOmsExportByBox.class, "Oms_BoxInventory_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public PageResult getProductLockingStockDetail(ProductStockLockingQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        //todo 添加次品处理等锁定
        List<StockLockingVo> list = baseMapper.getProductLockingStockDetail(bo);
        PageInfo<StockLockingVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(list, current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public PageResult getBoxLockingStockDetail(BoxStockLockingQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        //todo 添加次品处理等锁定
        List<StockLockingVo> list = baseMapper.getBoxLockingStockDetail(bo);
        PageInfo<StockLockingVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(list, current, pageSize, (int) pageInfo.getTotal(), pageInfo.getPages());
    }

    private IPage<SkuStockTotalDto> pageInitD(Map<String, Object> map) {
        IPage<SkuStockTotalDto> page = new Page();
        page.setSize(map.get("pageSize") != null ? Long.parseLong(map.get("pageSize").toString()) : 10);
        page.setCurrent(map.get("current") != null ? Long.parseLong(map.get("current").toString()) : 1);
        return page;
    }

    public void stockToPlatform(Long storeId) {
        //自动回传店铺 和sku
        List<Long> storeIdList = new ArrayList<>();
//        Object storeStr = redisTemplate.opsForValue().get(RedisKeyConstants.Oms_PlatformStock_Auto_StoreList + customer);
//        if (storeStr != null) {
//            String[] split = storeStr.toString().split("__");
//            storeIdList = Arrays.stream(split).map(Long::parseLong).collect(Collectors.toList());
//        }
//        if (storeId != null && !storeIdList.contains(storeId)) {
//            return;
//        }
        List<String> platformSkuList = new ArrayList<>();
        Object skuStr = redisTemplate.opsForValue().get(RedisKeyConstants.Oms_PlatformStock_Auto_SkuList + customer);
        if (skuStr != null) {
            String[] split = skuStr.toString().split("__");
            platformSkuList = Arrays.stream(split).collect(Collectors.toList());
        }
        List<String> platforms = new ArrayList<>();
        Object platformStr = redisTemplate.opsForValue().get(RedisKeyConstants.Oms_PlatformStock_Auto_PlatformList + customer);
        if (platformStr != null) {
            String[] split = platformStr.toString().split("__");
            platforms = Arrays.stream(split).collect(Collectors.toList());
        }
        if (storeId != null) {
            storeIdList.clear();
            storeIdList.add(storeId);
        }
        this.stockToPlatformNew(platforms, storeIdList, platformSkuList, null, 1, null, null);
    }

    @Override
    public void stockToPlatform() {
        this.stockToPlatform(null);
    }

    public void stockToPlatformNew(List<String> platforms, List<Long> storeIdList, List<String> platformSkuList, List<String> parentSkuList, Integer inventoryLink, Long userId, String userName) {
        if (CollUtil.isEmpty(storeIdList)) {
            return;
        }
        List<PlatformStoreDo> storeDoList = platformStoreDao.selectBatchIds(storeIdList);
        Map<Long, Long> storeJobIdMap = new HashMap<>();
        for (PlatformStoreDo storeDo : storeDoList) {
            if (storeDo.getAuthSyncStatus() != 1) {
                continue;
            }
            if (!Objects.equals(StaticDict.Company_Type.KK.getValue(), customer) && !platforms.contains(storeDo.getPlatformCode())) {
                continue;
            }
            //发送到队列
            AsyncJobDo asyncJobDo = new AsyncJobDo();
            asyncJobDo.setStatus(0);
            asyncJobDo.setCompanyId(storeDo.getCompanyId());
            asyncJobDo.setStoreId(storeDo.getId());
            asyncJobDo.setJobType("STOCK_TO_PLATFORM_PRODUCT");
            asyncJobDo.setJobName("库存回传_" + DateUtils.dateFormatToString(new Date(), "yyyy-MM-dd_HHmmss"));
            asyncJobDo.setCreateTime(new Date());
            asyncJobDo.setCreateBy(userName);
            asyncJobDao.insert(asyncJobDo);
            storeJobIdMap.put(storeDo.getId(), asyncJobDo.getId());
        }
        for (PlatformStoreDo storeDo : storeDoList) {
            Long jobId = storeJobIdMap.get(storeDo.getId());
            if (jobId == null) {
                continue;
            }
            if (StaticDict.Store_PlatformCode.Rakuten.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_RakutenNew, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), platformSkuList, parentSkuList, inventoryLink, jobId, userId, userName)));
            } else if (StaticDict.Store_PlatformCode.Yahoo.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_YahooNew, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), platformSkuList, parentSkuList, inventoryLink, jobId, userId, userName)));
            } else if (StaticDict.Store_PlatformCode.Amazon.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_AmazonNew, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), platformSkuList, parentSkuList, inventoryLink, jobId, userId, userName)));
            } else if (StaticDict.Store_PlatformCode.Wowma.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_WowmaNew, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), null, parentSkuList, inventoryLink, jobId, userId, userName)));
            } else if (StaticDict.Store_PlatformCode.Qoo10.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_Qoo10New, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), null, parentSkuList, inventoryLink, jobId, userId, userName)));
            } else if (StaticDict.Store_PlatformCode.SuperDelivery.getValue().equals(storeDo.getPlatformCode())) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_SuperDelivery, JSON.toJSONString(new StockToPlatformConsumerReq(storeDo.getId(), platformSkuList, parentSkuList, inventoryLink, jobId, userId, userName)));
            }
        }
    }

    /**
     * 库存回传至平台
     */
    @Override
    public void stockToPlatform(List<String> platforms, List<Long> storeIdList, List<String> platformSkuList, Integer inventoryLink, Long userId, String userName) {
        log.info("库存回传至平台: " + platforms + " " + storeIdList + " " + platformSkuList);
        if (CollUtil.isEmpty(platforms) || CollUtil.isEmpty(storeIdList)) {
            return;
        }
//        List<String> platforms = Arrays.asList(StaticDict.Store_PlatformCode.Amazon.getValue(), StaticDict.Store_PlatformCode.Rakuten.getValue(), StaticDict.Store_PlatformCode.Yahoo.getValue());
        //算上在途预售   parentSku is null 的判断 and t1.parent_sku is not null
        List<PlatformSkuStockDto> platformSkuStockList = baseMapper.getPlatformSkuStockList(platforms, platformSkuList, null, inventoryLink, storeIdList);
        log.info("库存回传至平台 平台sku库存列表长度: " + platformSkuStockList.size());
        Map<Long, List<PlatformSkuStockDto>> collect = platformSkuStockList.stream().filter(i -> i.getLeftQty() != null).collect(Collectors.groupingBy(PlatformSkuStockDto::getStoreId));
        for (Long storeId : collect.keySet()) {
            PlatformStoreDo storeDo = platformStoreDao.selectById(storeId);
            if (storeDo == null) {
                continue;
            }
            List<PlatformSkuStockDto> skuStockList = collect.get(storeId);
            if (StaticDict.Store_PlatformCode.Yahoo.getValue().equals(storeDo.getPlatformCode())) {
                List<YahooSkuSetStockReq> skuSetStockList = new ArrayList<>();
                for (PlatformSkuStockDto skuStock : skuStockList) {
                    if (StringUtils.isBlank(skuStock.getParentSku())) {
                        continue;
                    }
                    double stockPercent = skuStock.getStockPercent() < 0 || skuStock.getStockPercent() > 1 ? 0.95 : 1 - skuStock.getStockPercent();
                    int quantity = (int) (skuStock.getLeftQty() / skuStock.getQty() * stockPercent);
                    skuSetStockList.add(new YahooSkuSetStockReq(skuStock.getId(), skuStock.getParentSku(), skuStock.getPlatformSku(), quantity + ""));
                }
                List<List<YahooSkuSetStockReq>> partition = ListUtil.partition(skuSetStockList, 1000);
                for (List<YahooSkuSetStockReq> skuSetStockReqs : partition) {
                    YahooStockToPlatReq platReq = new YahooStockToPlatReq(skuSetStockReqs, storeDo, userId);
                    rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_Yahoo, JSON.toJSONString(platReq));
                }
            } else if (StaticDict.Store_PlatformCode.Rakuten.getValue().equals(storeDo.getPlatformCode())) {
                List<InventoryRakutenReq> stockList = new ArrayList<>();
                for (PlatformSkuStockDto skuStock : skuStockList) {
                    if (StringUtils.isBlank(skuStock.getParentSku())) {
                        continue;
                    }
                    if (userId == null && skuStock.getHideItem() != null && skuStock.getHideItem() == 1) {
                        continue;
                    }
                    double stockPercent = skuStock.getStockPercent() < 0 || skuStock.getStockPercent() > 1 ? 0.95 : 1 - skuStock.getStockPercent();
                    int quantity = (int) (skuStock.getLeftQty() / skuStock.getQty() * stockPercent);
                    quantity = Math.min(Math.max(quantity, 0), 99999);
                    stockList.add(new InventoryRakutenReq(skuStock.getId(), skuStock.getParentSku(), skuStock.getPlatformSku(), skuStock.getVariantId(), "ABSOLUTE", quantity));
                }
                List<List<InventoryRakutenReq>> partition = ListUtil.partition(stockList, 400);
                for (List<InventoryRakutenReq> skuSetStockReqs : partition) {
                    RakutenStockToPlatReq platReq = new RakutenStockToPlatReq(skuSetStockReqs, storeDo, userId);
                    rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_Rakuten, JSON.toJSONString(platReq));
                }
            } else if (StaticDict.Store_PlatformCode.Amazon.getValue().equals(storeDo.getPlatformCode())) {
                List<InventoryAmazonEntity> messageList = new ArrayList<>();
                for (int i = 0; i < skuStockList.size(); i++) {
                    PlatformSkuStockDto skuStock = skuStockList.get(i);
                    double stockPercent = skuStock.getStockPercent() < 0 || skuStock.getStockPercent() > 1 ? 0.95 : 1 - skuStock.getStockPercent();
                    int quantity = (int) (skuStock.getLeftQty() / skuStock.getQty() * stockPercent);
                    InventoryAmazonEntity message = new InventoryAmazonEntity(skuStock.getId(), skuStock.getParentSku(), skuStock.getPlatformSku(), quantity, skuStock.getShippingDayId());
                    messageList.add(message);
                }
                rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_Amazon, JSON.toJSONString(new AmazonStockToPlatReq(messageList, storeDo, userId)));

            }

        }


    }


    /**
     * 手动批量回传庫存
     */
    @Override
    public void manalStockToPlat(StockToPlatformReq req, Long userId, String userName) {
        if (req.getSkus() == null || req.getSkus().isEmpty()) {
            return;
        }
        List<Long> storeIdList = req.getSkus().stream().map(StockToPlatformManualReq::getStoreId).collect(Collectors.toList());
        if (storeIdList.isEmpty()) {
            return;
        }
        Map<Long, String> codeMap = platformStoreDao.selectList(new LambdaQueryWrapper<PlatformStoreDo>().in(PlatformStoreDo::getId, storeIdList))
                .stream().collect(Collectors.toMap(PlatformStoreDo::getId, PlatformStoreDo::getPlatformCode));
        Map<Long, List<StockToPlatformManualReq>> collect = req.getSkus().stream().collect(Collectors.groupingBy(StockToPlatformManualReq::getStoreId));
        for (Long storeId : collect.keySet()) {
            List<String> skus = collect.get(storeId).stream().map(StockToPlatformManualReq::getPlatformSku).collect(Collectors.toList());
            List<String> parentSkus = collect.get(storeId).stream().map(StockToPlatformManualReq::getParentSku).filter(StringUtils::isNotBlank).collect(Collectors.toList());
            if (skus.isEmpty()) {
                continue;
            }
            List<String> platforms = Arrays.asList(codeMap.get(storeId));
            this.stockToPlatformNew(platforms, Arrays.asList(storeId), skus, parentSkus, null, userId, userName);
        }
    }


    @Override
    public List<ReplenishmentPlanDo> productByIds(Map map) {
        Long userId = (Long) map.get("userId");
        String creator = baseMapper.selectCreator(userId);
        //当前时间 yyyy-MM-DD HH:mm:ss
        String now = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        String batchNumber = String.valueOf(System.currentTimeMillis());
        //将ids中的数据赋值给orderDoList
        ReplenishmentPlanOrderDo orderDo = new ReplenishmentPlanOrderDo();
        orderDo.setBatchNumber(batchNumber);
        orderDo.setCreator(creator);
        orderDo.setCreateTime(now);
        orderDo.setStatus(0);

        baseMapper.insertReplenishmentOrder(orderDo);

        List<ReplenishmentPlanDo> lists = (List<ReplenishmentPlanDo>) map.get("lists");
        for (ReplenishmentPlanDo list : lists) {
            list.setCreator(creator);
            list.setOrderId(orderDo.getId());
        }
        if (CollUtil.isNotEmpty(lists)) {
            baseMapper.insertBatchReplenishmentPlan(lists);
        }
        return lists;
    }


    @Override
    public PageResult<ReplenishmentPlanVo> supplementPlanListPage(Map<String, Object> map) {
        int currentPage = map.get("current") != null ? Integer.parseInt(map.get("current").toString()) : 1;
        int pageSize = (map.get("pageSize") != null) ? Integer.parseInt(map.get("pageSize").toString()) : 20;
        // 获取所有数据
        List<ReplenishmentPlanVo> replenishmentPlanVos = baseMapper.selectReplenishmentOrderVo(map);
        replenishmentPlanVos.sort(Comparator.comparing(ReplenishmentPlanVo::getCreateTime).reversed());
        // 计算总记录数和总页数
        int total = replenishmentPlanVos.size();
        int pages = (int) Math.ceil((double) total / pageSize);
        // 计算当前页的数据
        int start = (currentPage - 1) * pageSize;
        int end = Math.min(start + pageSize, total);
        List<ReplenishmentPlanVo> pageList = replenishmentPlanVos.subList(start, end);
        return new PageResult<>(pageList, currentPage, pageSize, total, pages);
    }


    @Override
    public void generateDocument(Map<String, Object> map) {
        List<Long> ids = (List<Long>) map.get("ids");
        if (CollUtil.isEmpty(ids)) {
            return;
        }

        Long userId = (Long) map.get("userId");
        String creator = baseMapper.selectCreator(userId);
        map.put("creator", creator);

        // 获取所有相关的 ReplenishmentPlanDo 对象
        List<ReplenishmentPlanDo> replenishmentPlanDos = baseMapper.selectByIds(ids);
        if (CollUtil.isEmpty(replenishmentPlanDos)) {
            return;
        }
        // 更新状态
        baseMapper.updateBatchStatus(ids);

        // 按 productId 分组
        Map<Long, List<ReplenishmentPlanDo>> groupedByProductId = replenishmentPlanDos.stream()
                .collect(Collectors.groupingBy(ReplenishmentPlanDo::getProductId));

        // 创建新的 ReplenishmentPlanDo 对象
        List<ReplenishmentPlanDo> newPlanDoList = new ArrayList<>();
        for (Map.Entry<Long, List<ReplenishmentPlanDo>> entry : groupedByProductId.entrySet()) {
            Long productId = entry.getKey();
            List<ReplenishmentPlanDo> planDoList = entry.getValue();

            // 计算总实际数量
            Integer totalAmount = planDoList.stream()
                    .mapToInt(ReplenishmentPlanDo::getActualAmount)
                    .sum();

            // 创建新的 ReplenishmentPlanDo 对象
            ReplenishmentPlanDo newPlanDo = new ReplenishmentPlanDo();
            BeanUtils.copyProperties(planDoList.get(0), newPlanDo);
            newPlanDo.setActualAmount(totalAmount);
            newPlanDo.setId(null);
            newPlanDoList.add(newPlanDo);
        }

        // 处理新的 ReplenishmentPlanDo 对象
        handleNewReplenishmentPlanVo(newPlanDoList, map);
    }


    @Override
    public void updateReplenishmentPlan(List<ReplenishmentPlanDo> replenishmentPlanDos) {
        baseMapper.updateBatchSingleReplenishmentPlan(replenishmentPlanDos);
    }


    //    @Override
    public void updateOrderStatus(List<Long> ids) {
        baseMapper.updateBatchStatus(ids);
    }

    @Override
    public void exportProduct(Map<String, Object> map, HttpServletResponse response) {
        List<String> stringProductIds = (List<String>) map.get("productIds");
        List<Long> productIds = null;
        if (stringProductIds != null) {

            productIds = stringProductIds.stream()
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
        }

        List<SkuStockDto> list = baseMapper.productBySku(map);
//保证excel导出数据和页面数据一致
        list = getSkuStockDtos(list, productIds);
        ExportParams exportParams = new ExportParams();
        // 设置样式
        try {
            // 确保ExcelUtils.exportExcel方法正确实现
            ExcelUtils.exportExcel(list, SkuStockDto.class, "库存状态", exportParams, response);
            // 手动刷新响应输出流
            response.flushBuffer();
        } catch (IOException e) {
            log.error("导出数据error", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void exportReplenishmentPlan(Map<String, Object> map, HttpServletResponse response) {

        List<ReplenishmentPlanVo> replenishmentPlanVos = baseMapper.selectReplenishmentOrderVo(map);

        String replenishmentPlans = (String) map.get("replenishmentPlans");
        if (replenishmentPlans != null) {
            replenishmentPlanVos = replenishmentPlanVos.stream()
                    .filter(replenishmentPlanVo -> replenishmentPlans.contains(replenishmentPlanVo.getBatchNumber()))
                    .collect(Collectors.toList());
        }
        // 添加null检查
        if (replenishmentPlanVos == null) {
            replenishmentPlanVos = new ArrayList<>();
        }

        // 稳定排序，处理null值
        replenishmentPlanVos.sort((o1, o2) -> {
            if (o1.getCreateTime() == null && o2.getCreateTime() == null) return 0;
            if (o1.getCreateTime() == null) return 1;
            if (o2.getCreateTime() == null) return -1;
            return o2.getCreateTime().compareTo(o1.getCreateTime());
        });

        //处理数据将ReplenishmentPlanVo转为ReplenishmentPlanExcelDto
        List<ReplenishmentPlanExcelDto> replenishmentPlanExcelDtos = new ArrayList<>();
        for (ReplenishmentPlanVo replenishmentPlanVo : replenishmentPlanVos) {
            // 添加null检查
            List<ReplenishmentPlanDo> replenishmentPlanDoList = replenishmentPlanVo.getReplenishmentPlanDoList();
            if (replenishmentPlanDoList == null) {
                replenishmentPlanDoList = new ArrayList<>();
            }

            for (ReplenishmentPlanDo replenishmentPlanDo : replenishmentPlanDoList) {
                ReplenishmentPlanExcelDto replenishmentPlanExcelDto = new ReplenishmentPlanExcelDto();
                replenishmentPlanExcelDto.setBatchNumber(replenishmentPlanVo.getBatchNumber());
                replenishmentPlanExcelDto.setCreator(replenishmentPlanVo.getCreator());
                replenishmentPlanExcelDto.setCreateTime(replenishmentPlanVo.getCreateTime());
                replenishmentPlanExcelDto.setStatus(replenishmentPlanVo.getStatus());
                // 添加null检查避免空指针
                if (replenishmentPlanDo != null) {
                    replenishmentPlanExcelDto.setSku(replenishmentPlanDo.getSku());
                    replenishmentPlanExcelDto.setSpu(replenishmentPlanDo.getSpu());
                    replenishmentPlanExcelDto.setProductName(replenishmentPlanDo.getProductName());
                    replenishmentPlanExcelDto.setFullPathName(replenishmentPlanDo.getFullPathName());
                    replenishmentPlanExcelDto.setRecommendAmount(replenishmentPlanDo.getRecommendAmount());
                    replenishmentPlanExcelDto.setActualAmount(replenishmentPlanDo.getActualAmount());
                }
                replenishmentPlanExcelDtos.add(replenishmentPlanExcelDto);
            }
        }

        ExportParams exportParams = new ExportParams();


        try {
            // 设置文件名编码，避免中文乱码
            String fileName = "补货计划_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName + ".xls", "UTF-8"));
            ExcelUtils.exportExcel(replenishmentPlanExcelDtos, ReplenishmentPlanExcelDto.class, fileName, exportParams, response);
        } catch (IOException e) {
            log.error("导出补货计划数据失败", e);
            throw new RuntimeException("导出数据失败", e);
        }
    }


    private static List<SkuStockDto> getSkuStockDtos(List<SkuStockDto> list, List<Long> productIds) {
        if (productIds != null) {
            //从list中找出productid在productIds中的
            list = list.stream().filter(i -> productIds.contains(i.getProductId())).collect(Collectors.toList());
        }

        for (SkuStockDto skuStockDto : list) {
            //保留两位小数
            if (skuStockDto.getDailySales() == 0.0) {
                skuStockDto.setDailySalesForExport("0");
            } else {
                skuStockDto.setDailySalesForExport(skuStockDto.getDailySales() + "");
            }

            skuStockDto.setThreeDaysSales(NumberFormater.formatDoubleTwoHalfUp(skuStockDto.getThreeDaysSales()));

            if (skuStockDto.getCanSellDay() == 0.0) {
                skuStockDto.setCanSellDayForExport("0");
            } else
                skuStockDto.setCanSellDayForExport(MathUtils.numberFormat(skuStockDto.getCanSellDay(), 2, RoundingMode.UP) + "");
            //保留整数，不要小数
            skuStockDto.setCanUseAmount(Math.round(skuStockDto.getCanUseAmount()));
            skuStockDto.setSales(Math.round(skuStockDto.getThreeDaysSales()) + "/" + Math.round(skuStockDto.getSevenDaysSales()) + "/" + Math.round(skuStockDto.getThirtyDaysSales()));
            Integer stockStatus = skuStockDto.getStockStatus();
            if (stockStatus != null) {
                switch (stockStatus) {
                    case 10:
                        skuStockDto.setStockStatusName("尽快补货");
                        break;
                    case 20:
                        skuStockDto.setStockStatusName("需要关注");
                        break;
                    case 30:
                        skuStockDto.setStockStatusName("滞销");
                        break;
                    case 40:
                        skuStockDto.setStockStatusName("库存充足");
                }
            }
        }
        return list;
    }


    private void handleNewReplenishmentPlanVo(List<ReplenishmentPlanDo> planDoList, Map<String, Object> map) {
        // 当前时间
        String now = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        // 创建批次号
        String batchNumber = String.valueOf(System.currentTimeMillis());

        // 创建新的 ReplenishmentPlanOrderDo 对象
        ReplenishmentPlanOrderDo replenishmentPlanOrderDo = new ReplenishmentPlanOrderDo();
        replenishmentPlanOrderDo.setBatchNumber(batchNumber);
        replenishmentPlanOrderDo.setCreator((String) map.get("creator"));
        replenishmentPlanOrderDo.setCreateTime(now);
        replenishmentPlanOrderDo.setStatus(0);

        try {
            // 插入新的订单
            baseMapper.insertNewOrder(replenishmentPlanOrderDo);

            // 获取插入后的订单ID
            Long orderId = replenishmentPlanOrderDo.getId();

            // 将 planDoList 中的每个对象关联到新的订单
            for (ReplenishmentPlanDo replenishmentPlanDo : planDoList) {
                replenishmentPlanDo.setOrderId(orderId);
            }

            // 批量插入 ReplenishmentPlanDo 对象
            if (CollUtil.isNotEmpty(planDoList)) {
                baseMapper.insertBatch(planDoList);
            }
        } catch (Exception e) {
            // 记录异常日志
            log.error("Failed to handle new replenishment plan VO", e);
            // 根据需要抛出异常或进行其他处理
            throw new RuntimeException("Failed to handle new replenishment plan VO", e);
        }
    }


}
